15

I have the following (simplified) Model:

class Zone(gismodels.Model):
    name = gismodels.CharField()
    poly = gismodels.PolygonField()

I want to create and save a polygon that represents a circle, based upon a given point and radius.

The only way I can figure out how to achieve this, is to call the postgis ST_Buffer function using raw SQL. I'm really hoping that there is another way.

Is it possible to access the GEOS buffer methods?

MattRowbum
  • 2,162
  • 1
  • 15
  • 20

2 Answers2

30

Yes, it is possible to use the geos buffer method:

>>> from django.contrib.gis import geos
>>> center = geos.Point(5, 5)
>>> radius = 2
>>> circle = center.buffer(radius)
>>> circle
<Polygon object at 0x1029d8370>

The radius here is in the same units as the coordinates of the points. This will work for some coordinate systems like UTM, but not as well for others.

Also, while this is appropriate for constructing a circular geometry, the PostGIS documentation notes that for doing radius searches ST_DWithin is more efficient.

tcarobruce
  • 3,773
  • 21
  • 33
  • 2
    Thanks for your answer. I have a radius in kilometers and my Point is created using a longitude/latitude pair in units of degrees. Do I need to convert my radius to degrees to get a useful result, or is there built-in functionality to handle this? – MattRowbum Feb 16 '11 at 22:52
  • 1
    @MattRowbum - You'll need to convert to degrees from km. Also, this will leave you with ovals that get more stretched the farther you get from the equator. PostGIS now allows a "geography" type which is more what you want, but I couldn't find a way to access it via the GEOS API in GeoDjango. – tcarobruce Feb 21 '11 at 03:21
  • 2
    How to convert kilometres to degrees: http://stackoverflow.com/questions/5217348/how-do-i-convert-kilometres-to-degrees-in-geodjango-geos – fmalina Jun 16 '13 at 16:28
1

I spent a ridiculous amount of time trying to get this working. Since this is the number one google search result, here's what worked for me:

radius_km = radius*1.609 # convert miles to km
point = target.geolocation # a django PointField using SRID 4326
# re-project point to a flat coordinate system 
# so we can use meters instead of degrees below, 
# AND get an actual circle instead of oval    
point.transform(6347) 
poly = point.buffer(radius_km*1000) # get a circular polygon from radius
poly.transform(4326)# re-project the resulting polygon back

Bonus: If you're doing this so you can get a circle on a google static map, grab polyline:

import polyline
import ast

geo = ast.literal_eval(poly.geojson) # turn the text into a dict
points = geo['coordinates'][0]
pl = polyline.encode(points, geojson=True, precision=5) # make a polyline out of the polygon for google
map_url += '&path=color:0x00000000%7Cfillcolor:0x0000AA33%7Cweight:1%7Cenc:' + pl
Psyferre
  • 135
  • 2
  • 9
  • Thank you! What does the magic number 6347 mean? – chistyakov Oct 12 '21 at 13:18
  • A year later: :D We're converting from a spheroid projection system to a flat projection system, getting our "circle", and then converting back. 6437 is a "flat" EPSG projection which uses meters as its base unit. 4326, on the other hand, is spheroid (?) and measured in degrees. That allowed me to use meters for my radius, which means the "circle" on the map is not an oval (which is what you get if you try to get a circular polygon based on degrees). After that's done, we re-project back to 4326. More: https://en.wikipedia.org/wiki/EPSG_Geodetic_Parameter_Dataset – Psyferre May 29 '22 at 00:57