如何使用GeoDjango / PostGIS对非地理(X,Y,Z)距离进行排序?

时间:2013-12-06 18:00:14

标签: django transform postgis geodjango srid

我目前正在“星搜索和排序”数据库中使用GeoDjango,该数据库提供有关星形和行星系统信息并将模拟其信息。

我正在使用GeoDjango 1)因为我喜欢并在其他地方使用它,2)因为我最终想要使用各种“地理”搜索功能,如距离/线条/多边形变换,以便进行复杂和酷炫的体积查询在其他地方找不到。

我启动并运行了系统(https://github.com/jaycrossler/procyon),它目前使用恒星银河坐标(已经从右上升/下降转换)。目前数据库中有150k星,我正在考虑增加到几百万。

在数据库中有一个星星之后,我构建了一个具有GeoDjango PointField的新表,然后用Galactic X,Y,Z坐标填充它(在parsecs中,这些坐标大多在-500范围内)到500)。现在,我已经将SRID设置为900913(这样我将有一个很好的坐标范围,不会在世界范围内滚动)...但是当我搜索附近的星星并按距离排序时,我只得到一条线上的回报,而不是基于X,Y,Z距离真正接近。

location = models.PointField(dim=3, blank=True, null=True, srid=900913)
objects = models.GeoManager()

我认为这是因为我所做的每一次搜索最终都被包裹在一个球体的表面上,这既低效又搞砸了结果(尽管如果它只使用一行代码进行搜索,我对它很酷)。

我在Django中使用的当前搜索是:

    origin = self.location

    distance = 1000
    close_by_stars = StarModel.objects.filter(location__distance_lte=(origin, D(m=distance))).distance(origin).order_by('distance')
    for s in close_by_stars[:200]:
      #export results

但是返回的结果并不是我所期望的(我认为它们会聚集在一颗恒星周围,而不是在一条线上),可视化: Stars visualized from search

所以,最大的问题是: 1)我应该使用SRID,如900913(球形墨卡托) 要么 2)是否存在未映射到行星表面的SRID,因此我可以在X,Y,Z距离上搜索而不会将-180滚动到+180(或基于投影系统的任何等效物) )?我尝试使用SRID = 0,但geodjango比pukes并不允许。

1 个答案:

答案 0 :(得分:1)

我有一个解决方案,我在这里分享可能会帮助他人。

我认为问题是'location__distance_lte ='函数在POSTGIS中转换为geo函数':ST_DWithin

从pg_log / latest看,我看到了SQL命令:

SELECT (ST_Distance("modelname"."location",
ST_GeomFromEWKB('\x01010000a031bf0d0021e527d53e2963409f3c2cd49ab2404081b22957787d5540'::bytea))) AS "distance",
"modelname"."info", "modelname"."location" 
FROM "modelname" WHERE ST_Distance("modelname"."location",
ST_GeomFromEWKB('\x01010000a031bf0d0021e527d53e2963409f3c2cd49ab2404081b22957787d5540'::bytea))
<= 10.0 ORDER BY "distance" ASC

因此,当通过X,Y,Z搜索并寻找最近的点时,它只在2D空间内搜索 - 并查找X,Y距离内的那些......而不是Z中的那些。

有一个ST_3DDWithin(http://postgis.net/docs/ST_3DDWithin.html),但不幸的是Django不知道它:https://github.com/django/django/blob/master/django/contrib/gis/db/backends/postgis/operations.py#L154

我可以使用原始sql方法而不是覆盖django源:https://docs.djangoproject.com/en/dev/topics/db/sql/#django.db.models.Manager.raw。 但是,那么orm会失去很多好处。

但相反,我决定更简单/更复杂。我保持相同的搜索(基本上返回“圆柱”而不是结果的范围)。然后,在函数中,我遍历结果并解析出不在球形结果中的结果:

origin = item.location
origin_array = numpy.array((origin.x, origin.y, origin.z))

close_by_stars = star_model.objects.filter(location__distance_lte=(origin, D(m=distance))).distance(origin).order_by('distance')

star_list = []
for s in close_by_stars:
    location_array = numpy.array((s.location.x, s.location.y, s.location.z))

    dist = numpy.linalg.norm(origin_array - location_array)
    if dist > distance:
        continue

    star_handle = dict()
    star_handle['data'] = s.data ...