Pergunta

stack has helped me out when I needed it the most and I'm going to test my luck again. Here's the scenario. Currently I have the following model:

class Location(models.Model):
    name = models.CharField(max_length=200, blank=True)
    address = models.CharField(max_length=255, blank=True)
    city = models.CharField(max_length=140, null=True, blank=True)
    province_state = models.CharField(max_length = 140, null=True, blank=True)
    country = models.CharField(max_length=2, null=True, blank=True)
    postal_code = models.CharField(max_length=32, null=True, blank=True)
    foursquare_id = models.CharField(max_length=100, blank=True, unique=True)
    lat = models.DecimalField(max_digits=18, decimal_places=15, null=True, blank=True)
    lon = models.DecimalField(max_digits=18, decimal_places=15, null=True, blank=True)

Looks fairly straight forward right? Now what I do is I return back all locations that are within 100 KM of a user's current location. Each degree is approximately 100 KM. So for example, between a latitude of 10 degrees and 11 degrees, it is about distance of 100 KM. Same with longitude. The 1 degree is approx ~ 100 km. Here is my WhatsNearBy view:

@csrf_exempt
def WhatsNearBy(request):
    if request.META.has_key('HTTP_AUTHORIZATION') and request.method == 'GET':
        authmeth, auth = request.META['HTTP_AUTHORIZATION'].split(' ', 1)
        if authmeth.lower() == 'basic':
            auth = auth.strip().decode('base64')
            username, password = auth.split(':', 1)
            authuser = authenticate(username=username, password=password)
            if authuser: 
                if authuser.is_active:
                client_lat = int(request.GET.get('lat'))
                client_lon = int(request.GET.get('lon'))

                queryset = Location.objects.filter(__lat__gte = (client_lat - 1), 
                                lat__lte = (client_lat + 1), 
                                lon__gte = (client_lon - 1), 
                                lon__lte = (client_lon + 1))
                return queryset

The above queryset returns back all the Locations that are approximately 100 KM away. This works great and it has been quite solid. However, my concern is that the filtering will sooner or later become slow as the database gets larger.

I heard about postGIS but I'm not sure if it would be more efficient than what I am doing now. Would I have to migrate the Location model to postGIS? how would this all work? and once I do would I be able to find all the locations within a certain distance?

Looking forward to seeing your solutions!

Foi útil?

Solução

I dealt with a similar problem recently. I found that PostGIS and other spatial solutions were overkill for finding all of the points in an approximate radius.

I ended up using the SAS distance algorithm in the database. After filtering with a square, I used the raw command to let the database crunch the numbers for me. Postgres and most other databases other than SQLite support trig functions, so you can filter on the database's computed distance. This is the formula in miles, but you can change the constant.

Dist = 3949.99 * arcos(sin(Y1) * sin(Y2) + cos(Y1) * cos(Y2) * cos(X1 - X2));

http://support.sas.com/kb/3/973.html

Edit: To expand, I was querying all zip codes (~44K) in the US within a radius. It turns out I could query all of the zip codes within 500 miles in fractions of a second that were acceptably fast for my purposes. The geospatial libraries allow you to find all of the points within a complex polygon much more quickly, but I don't expect you'll see a noticeable performance improvement for your application.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top