Take the 2-minute tour ×
Geographic Information Systems Stack Exchange is a question and answer site for cartographers, geographers and GIS professionals. It's 100% free, no registration required.

I'm using geodjango + postgres to display polygons from tigerline on a map. Pretty simple stuff so far.

My issue is that when I use the GPolygon object: django.contrib.gis.maps.google.GPolygon there is a considerable slowdown. (see below for the code I'm using)

locations = Location.objects.filter(mpoly__contains=point)
polygons = []
for location in locations :
    for poly in location.mpoly :
        gpoly = GPolygon(poly, \
            stroke_color = location.location_type.stroke_color,\
            stroke_weight = location.location_type.stroke_weight,
            stroke_opacity  = location.location_type.stroke_opacity,\
            fill_color = location.location_type.fill_color,\
            fill_opacity = location.location_type.fill_opacity)
        gpoly.location = location.id
        #raise Exception(gpoly)
        polygons.append(gpoly)
# Google Map Abstraction
#raise Exception(len(polygons))
the_map = GoogleMap(polygons=polygons)
  • all of my location's mpoly are MultiPolygons
  • there are only 84 polygons total when I raise Exception(len(polygons))
  • this tiny block of code is incurring a 10 sec load time on localhost w/ 4gig ram and an i5 proc... I'm not resource locked

Does anybody have any idea what GPolygon is doing? Is GPolygon not ideal for production? is it just for prototyping?

EDIT

I'm now setting up my GPolygon like so:

gpoly = GPolygon(poly.simplify(float(get_tolerance(poly.num_points))), \
    stroke_color = location.location_type.stroke_color,\
    stroke_weight = location.location_type.stroke_weight,
    stroke_opacity  = location.location_type.stroke_opacity,\
    fill_color = location.location_type.fill_color,\
    fill_opacity = location.location_type.fill_opacity)

using the function:

def get_tolerance(num_points) :
    """figure out a good tolerance"""
    tolerance = 0
    if num_points <= 500:
        tolerance = 0
    elif num_points <= 750: 
        tolerance = .001
    elif num_points <= 1000: 
        tolerance = .002
    elif num_points > 2000: 
        tolerance = .007

    return tolerance

which sets a tolerance for the simplify geos function based on the total number of points (I noticed that for things like florida where the keys(islands) are separate polygons that the ones with fewer points broke with a high tolerance, while the big main landmass polygons are too huge without simplifying to a large degree.

This has made my program a magnitude faster BUT I still think there is tons of room for improvement.

Which leads me to my newest questions:

  • Is there a better way to guess acceptable tolerances?
  • What other possible speed gains could I explore?

UPDATE

Something i did was pass all of the polygons to the view as geojson objects and use JS to build the polygon objects which is way faster than django.contrib.gis.maps.google.GPolygon.

Server side python:

@csrf_exempt
def get_location_polygons(request):
    """Returns a civic location name from a geodetic point"""
    response_data = {'ack':None,'data':None,'messages':[]}
    if request.method == 'POST':
        #try :
        # Get the location
        location = Location.objects.get(pk=request.POST['location_id'])

        # Build the polygons
        response_data['ack']            = True
        response_data['messages'] = _(u'OK')            
        response_data['data']           = {
            'stroke_color'      : location.location_type.stroke_color,
            'stroke_weight'     : location.location_type.stroke_weight,
            'stroke_opacity'    : location.location_type.stroke_opacity,
            'fill_color'            : location.location_type.fill_color,
            'fill_opacity'      : location.location_type.fill_opacity,
            'polygons'              : location.mpoly.geojson,
            'title'                     : location.title
        }
        #except :
        #   # Fetch failed
        #   response_data['ack']            = False
        #   response_data['messages'] = [_(u'Polygon for location could not be fetched.')]
    else:
        response_data['ack']            = False
        response_data['messages'] = [_(u'HTTP Method must be POST')]

    return HttpResponse(json.dumps(response_data), mimetype="application/json")

Client side JS:

poly = JSON.parse(data.data['polygons'])
var paths = coord_to_paths(poly.coordinates, bucket, location_id);
polygons[bucket][location_id] = new google.maps.Polygon({ 
    paths                   : paths, 
    strokeColor     : data.data.stroke_color, 
    strokeOpacity   : data.data.stroke_opacity, 
    strokeWeight    : data.data.stroke_weight, 
    fillColor           : data.data.fill_color, 
    fillOpacity     : data.data.fill_opacity
});
function coord_to_paths(coords, bucket, location_id)
{
    var paths = []; 
    poly_bounds[bucket][location_id] = new google.maps.LatLngBounds();
    for (var i = 0; i < coords.length; i++)
    { 
        for (var j = 0; j < coords[i].length; j++)
        { 
            var path = []; 
            for (var k = 0; k < coords[i][j].length; k++)
            { 
                var ll = new google.maps.LatLng(coords[i][j][k][1], coords[i][j][k][0]);
                poly_bounds[bucket][location_id].extend(ll);
                path.push(ll); 
            } 
            paths.push(path); 
        } 
    }

    return paths;       
}
share|improve this question

migrated from stackoverflow.com Jun 12 '12 at 16:36

This question came from our site for professional and enthusiast programmers.

    
Thanks for migrating this question over. I may leave it open for a while and see if anyone has a better solution than what I came up with. Thanks for taking interest. –  Francis Yaconiello Jun 12 '12 at 17:43

1 Answer 1

up vote 3 down vote accepted

Reducing fidelity (i.e removing the number of nodes) will help since there is less data to pass to Google Maps.

Nevertheless, I would hope you are not doing this for every request directly in the view and that this is something that you are doing only once (during the first save), or through some asynchronous queue mechanism like Celery.

You can always have a shape that you use for analysis (with the full vertex count) and another one that you use for display.

share|improve this answer
    
I figured reducing the number of vertices would be key, however, choosing the simplification factor for each polygon seems not as straight forward. any idea on a good algorithm for that? my get_tolerance(num_points) function sometimes breaks if it tries to simplify a poly too much. Thanks for responding –  Francis Yaconiello Jun 15 '12 at 14:57
    
get_tolerance should not be a function based on the number of nodes, you should just pick a number and stick with it. The tolerance value determines the degree of simplification and it uses the units of the underlying spatial reference. It is not exactly that, but you can think of it as the minimum distance before two nodes are close enough that they become one. –  Ragi Yaser Burhum Jun 15 '12 at 16:23
    
that is interesting, I think the simplify function in geodjango may be buggy then. On smaller polygons I noticed that If i specified a tolerance too high that the polygon would break (not validate as a geos polygon) I figured that had to do with oversimplification and that I should adjust the simplification level based on the number of nodes (which worked to a degree, but seemed error prone) –  Francis Yaconiello Jun 15 '12 at 18:12
    
I doubt is a bug in geodjango. Probably you are using values that are too high. Remember that in order to be a valid polygon, you need to have at least 3 points. If your value is too high then you can run into cases where too many nodes snap to each other and you end up with less than three nodes. Many nodes does not mean bigger feature necessarily, it just means higher fidelity / density. I can create a super small polygon with 1000 nodes that will blow up when you use 0.02 of tolerance - no bug in geodjango required. –  Ragi Yaser Burhum Jun 15 '12 at 19:14
    
in that case I'm back to my original question, how do you determine programatically the correct tolerance? –  Francis Yaconiello Jun 15 '12 at 19:21

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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