2

I'm working on a project where I need to visualize a constantly updating line of bearing from a point, based on degree of rotation around that point. My plan is to design a line feature with an attribute table that links to a CSV which is being fed this rotation data every few seconds. I'll run a python script that creates a new line feature each time the CSV is updated, visualizing a max of 10 lines, removing the oldest line per update.

My big hurdle right now is figuring out how to make the generated lines rotate around the relevant point through maybe some sort of snapping feature.

Is this possible?

I feel like there's a much easier way to do this than the method I'm proposing, but I can't think of what it might be. This is ultimately going to be ported to a separate window to a UI outside of QGIS, so I'm worried I'm going about this the wrong way.

I've come across some similar questions that touch on what I'm getting at, but nothing that's truly addressed this specific need. I've also tried working with the Distance and Azimuth plugin, but it works in a way that wouldn't cooperate with my aforementioned method. I need the lines to snap to an independent point feature with separate attributes. Or maybe I'm wrong and this can be done.

1

Creating spokes radiating around a central point is certainly doable in Qgis 2.1 and Python.

Creating radial lines from a point

Here's some python code which will shows how to create radial lines, given

  • the name of a point layer,
  • the id of the point
  • the name of a line layer to receive the 'spokes'.

It creates 360 lines, a degree apart, centered on the point, with a line length of 0.01 (degrees).

import math
from qgis.core import QgsFeature

'''
Names of layers
'''
pointLayerName = "fan points"
lineLayerName = "line points"

'''
find layer objects by name
'''
layers = QgsMapLayerRegistry.instance().mapLayers()
for name, layer in layers.iteritems():
    print "%s %s" % (layer.name(),layer)
    if layer.name()==lineLayerName:
        lineLayer = layer
    if layer.name()==pointLayerName:
        pointLayer = layer

assert pointLayer!=None
assert lineLayer!=None

'''
find the point (by id) we want
'''
expr = QgsExpression( "id=1" )
it = pointLayer.getFeatures( QgsFeatureRequest( expr ) )
for f in it: 
  geompoint = f.geometry()
  pivot = geompoint.asPoint()
  break # only want first matching feature

'''
Makes spokes.
'''

lineLayer.beginEditCommand("Add spokes")
provider = lineLayer.dataProvider()
linefeatures=[]
for ang in range(0,360,1):
    geomspoke = QgsGeometry.fromPolyline([QgsPoint(0.0,0.0),QgsPoint(0.0,.01)])
    geomspoke.rotate(float(ang),QgsPoint(0.0,0.0))
    geomspoke.translate(pivot.x(),pivot.y())
    print geomspoke.exportToWkt(6)
    spokefeat = QgsFeature()
    spokefeat.setGeometry(geomspoke)
    linefeatures.append(spokefeat)
provider.addFeatures(linefeatures)
lineLayer.commitChanges()
lineLayer.endEditCommand()
print "Done!"

Note that I've used a couple of methods which may require a fairly recent version of QGIS (they're there in 2.1 but may not be in earlier versions like 1.8)

one degree spokes from a central point

Your use case

You should be able to modify this to read in angles from your data source to add these spokes as data comes in.

By the sounds of it you only want to keep a fixed number of lines, so a Python FIFO (first-in-first-out buffer, such as a deque) could be used to store the QgsFeatures for the spokes, and recreate the line layer for each 'frame' from the contents of this buffer. Each time you receive some data, drop the oldest and add in the newest.

Animating

Animating is more tricky. Normally I'd suggest TimeManager plugin, but that might not be suitable considering that you're going to be implementing this as a stand-alone application.

However, TimeManager does do something you need - updating and re-rendering a QGSMapCanvas, showing features according to a specific query (which features have the current time value for this frame?).

So you'd probably find the source a good starting point to see how to do this, or at least to try a proof-of-concept before coding this up standalone. (Tip: you can use unix timestamps as a time marker for each frame, and 1970-01-01 00:00:00 to correspond to a value of 0)

Hope this helps!

  • Thanks for your thorough and informative response. I'm going to take this code you've suggested and try to work with it a little. Hopefully it points me in the right direction. My goal now is to take the code you've provided and try to figure out how to make it draw lines according to input degree. Not a clue where to begin! – Alex92 Sep 8 '15 at 14:52
1

UPDATE: I think I've resolved this! The key is in symbolizing my lines of bearing as overlapping polar Vector Field markers.

I symbolized my lines of bearing as points from a text-delimited layer, then turned the points into lines by changing the symbol layer type to a Vector Field marker. I set the the Vector type to polar, which allowed me to set my rotation field to the rotation field of the CSV I'm linked to. Now, the "points" appear as lines emitting from a central point.

However, my problem now is that the lines disappear when the centerpoint goes offscreen. The line distance is purely artifical too, and dependent on map extent, so I won't be able to do any geoprocessing over them if I wanted to. Perhaps I've just macguivered this thing once more... I think I still need authentic line features.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.