之前做個項目,我們的項目中有用戶添加的特定信息,這條信息包含了用戶添加時的位置(經緯度),現在有個需求,就是后取當前用戶30km范圍內所有的這種信息。思路並不難,但是有個問題,如果我們每次都根據用戶的當前位置,然后對比所有信息的經緯度坐標,計算出用戶30km范圍的數據,再返回給用戶,我們需要很好的去優化算法和查詢方式,但是依我目前的水平,還做不到很好的優化。由於項目使用的數據庫是postgresql,於是就想到了postgis。
PostGIS is a spatial database extender for PostgreSQL object-relational database.
GIS(Geographic Information System),空間地理信息系統。
django中如何使用postgis(https://docs.djangoproject.com/en/2.1/ref/contrib/gis/tutorial/)
通過django 官方文檔可知,Ubuntu/Debian安裝postgis是非常方便的,只需要使用使用apt-get install 命令安裝以下包(注意:postgis只支持postgresql9.3或以上版本https://postgis.net/docs/postgis_installation.html#install_requirements):
postgresql-x.x, postgresql-x.x-postgis, postgresql-server-dev-x.x, python-psycopg2
(2019-01-20 postgresql 10,然后直接sudo apt install libgdal-dev 環境就可以了,不用上面的那么麻煩了)
數據庫不要忘記安裝extension:CREATE EXTENSION postgis;
使用前配置請參考:django postgis Configure settings.py
對於之前提出的需求,我是這樣做的:
model:
from django.contrib.gis.db import models as geo_models from django.contrib.gis.db.models.manager import GeoManager class PatrolPoint(models.Model): id = models.BigAutoField(primary_key=True) appid = models.CharField(max_length=200, null=True, blank=True, default='') user = models.ForeignKey(user_info) river_base_info = models.ForeignKey(RiverBaseInfo, null=True) river_check = models.ForeignKey(RiverCheck, null=True) task = models.ForeignKey(tasks, null=True) longitude = models.CharField(max_length=50, blank=True, null=True) latitude = models.CharField(max_length=50, blank=True, null=True) symbol = models.CharField(max_length=125, blank=True, null=True) river_name = models.CharField(max_length=125, blank=True, null=True) create_time = models.DateTimeField(blank=True, auto_now_add=True, null=True) update_time = models.DateTimeField(blank=True, auto_now=True) pos = geo_models.GeometryField(srid=4326, null=True) usual_flag = models.BooleanField(default=True) report_flag = models.BooleanField(default=False) objects = GeoManager() class Meta: db_table = "patrol_point"
查詢語句:
from django.contrib.gis.geos import GEOSGeometry from django.contrib.gis.db.models.functions import Distance patrol_points = PatrolPoint.objects.annotate(distance=Distance('pos', GEOSGeometry('POINT(%s %s)' % (request.GET['longitude'],request.GET['latitude']), 4326))).filter(distance__lte=radius, usual_flag=True)