Google Maps Polyline提取与Buffer操作断言失败问题求助
解决Django+PostGIS项目中生成路线缓冲区时的Assertion Failed错误
嘿,我看你在开发Django项目时,遇到了Assertion failed: (0), function query, file AbstractSTRtree.cpp, line 287这个头疼的错误,结合你的代码和需求,我来帮你一步步梳理问题并解决:
首先,先修正模型里的明显错误
你的Route模型字段有几个低级错误,这可能会间接导致后续的问题:
startPoint_address和endPoint_address是用户输入的字符串地址,应该用CharField,不是PointFieldPointfield拼写错了,应该是PointField(注意大写的F)- 导入
django.contrib.gis.db.models后就不需要再导入普通的models了,GIS模块已经包含了所有字段类型
修正后的模型代码:
from django.contrib.gis.db import models class Route(models.Model): startPoint_address = models.CharField(max_length=255) startPoint_geolocation = models.PointField() endPoint_address = models.CharField(max_length=255) endPoint_geolocation = models.PointField() routeName = models.CharField(max_length=50, blank=True) deviationDistance = models.IntegerField(default=1000) # 默认1km缓冲区
分析错误的核心原因
你遇到的这个断言错误,大概率是两个问题导致的:
- 缓冲区单位错误:Google返回的坐标是WGS84(EPSG:4326),单位是度,你用
buffer(0.00001)相当于只缓冲了约1米的距离,这个值太小了,会让Shapely生成无效的多边形,触发底层的空间索引错误。 - 几何对象无效:解码后的polyline可能存在重复点、自相交的情况,也会导致缓冲区计算失败。
正确的缓冲区生成流程(转投影坐标系)
因为WGS84是地理坐标系,直接做缓冲区距离不准确(1度≈111km),所以必须先转换到投影坐标系(比如UTM,单位是米),生成1km缓冲区后再转回WGS84:
我帮你重写了核心逻辑,还去掉了没必要的自定义解码函数(Django GIS已经内置了这个功能):
import shapely.geometry import shapely.ops import googlemaps from django.contrib.gis.geos import Point, Polygon, LineString # 初始化Google Maps客户端 gmaps = googlemaps.Client(key='AIzaSyCetShZ10WQVBVPaIu9pdPEktRVQq78TF4') # 获取路线数据 google_directions = gmaps.directions("Stellenbosch", "Cape Town") encoded_polyline = google_directions[0]['overview_polyline']['points'] # 用Django GIS内置方法解码polyline,比自己写的可靠 decoded_line = LineString.from_encoded(encoded_polyline) # 先修复可能无效的几何(比如自相交、重复点) if not decoded_line.valid: decoded_line = decoded_line.buffer(0) # 自动匹配合适的UTM投影带(根据路线中心点) center = decoded_line.centroid utm_zone = int((center.x + 180) / 6) + 1 # 南半球用327xx,北半球用326xx utm_crs = f'EPSG:326{utm_zone}' if center.y >= 0 else f'EPSG:327{utm_zone}' # 把路线转换到UTM坐标系(单位米) projected_line = shapely.ops.transform( lambda x, y: Point(x, y).transform(utm_crs, clone=True).coords[0], decoded_line ) # 生成1km缓冲区(这里的1000就是米) buffered_line = projected_line.buffer(1000) # 转换回WGS84坐标系,方便存入PostGIS buffered_wgs84 = shapely.ops.transform( lambda x, y: Point(x, y).transform('EPSG:4326', clone=True).coords[0], buffered_line ) # 转为Django GIS的Polygon对象,直接存数据库 django_polygon = Polygon(buffered_wgs84.exterior.coords)
额外的注意事项
- 永远不要自己写polyline解码函数,Django GIS的
LineString.from_encoded()已经经过测试,比自定义实现更稳定。 - 确保你的PostGIS数据库已经正确配置,并且运行过
makemigrations和migrate命令,创建了对应的GIS字段。 - 如果还是遇到几何无效的问题,可以在生成缓冲区前强制修复几何:
decoded_line = decoded_line.simplify(0.00001)(简化冗余点)。
完整的视图示例(处理用户输入)
假设你要在视图里处理用户提交的起点和终点,这里给你一个参考:
from django.shortcuts import render from .models import Route def create_route(request): if request.method == 'POST': start_addr = request.POST.get('start') end_addr = request.POST.get('end') gmaps = googlemaps.Client(key='AIzaSyCetShZ10WQVBVPaIu9pdPEktRVQq78TF4') # 获取起点和终点的经纬度 start_geo = gmaps.geocode(start_addr)[0]['geometry']['location'] end_geo = gmaps.geocode(end_addr)[0]['geometry']['location'] # 获取路线polyline并处理缓冲区 directions = gmaps.directions(start_addr, end_addr) encoded_polyline = directions[0]['overview_polyline']['points'] decoded_line = LineString.from_encoded(encoded_polyline) if not decoded_line.valid: decoded_line = decoded_line.buffer(0) center = decoded_line.centroid utm_zone = int((center.x + 180) / 6) + 1 utm_crs = f'EPSG:326{utm_zone}' if center.y >=0 else f'EPSG:327{utm_zone}' projected_line = shapely.ops.transform( lambda x, y: Point(x, y).transform(utm_crs, clone=True).coords[0], decoded_line ) buffered_line = projected_line.buffer(1000) buffered_wgs84 = shapely.ops.transform( lambda x, y: Point(x, y).transform('EPSG:4326', clone=True).coords[0], buffered_line ) django_polygon = Polygon(buffered_wgs84.exterior.coords) # 保存到数据库 Route.objects.create( startPoint_address=start_addr, startPoint_geolocation=Point(start_geo['lng'], start_geo['lat']), endPoint_address=end_addr, endPoint_geolocation=Point(end_geo['lng'], end_geo['lat']), # 如果你的模型里有存储缓冲区的字段,比如: # route_buffer=django_polygon ) return render(request, 'route_success.html') return render(request, 'route_form.html')
内容的提问来源于stack exchange,提问作者Petrus
相关产品推荐
相关产品推荐

