django postgis 初步使用
之前做个项目,我们的项目中有用户添加的特定信息,这条信息包含了用户添加时的位置(经纬度),现在有个需求,就是后取当前用户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)