GeoAlchemy2: 找到一组不与另一组相交的几何项
我有一个叫做 tasks
的PostGIS数据库表,这个表通过geoalchemy2/sqlalchemy映射到一个Python类 Task
。每一条记录都有一个MultiPolygon类型的 geometry
和一个整数类型的 state
。这些记录一起覆盖了一个地理区域。我想要随机选择一个状态为0的记录,并且这个记录不能和任何状态为1的记录相邻。
下面是选择随机状态为0的记录的代码:
class Task(Base):
__tablename__ = "tasks"
id = Column(Integer, primary_key=True, index=True)
geometry = Column(Geometry('MultiPolygon', srid=4326))
state = Column(Integer, default=0)
session = DBSession()
taskgetter = session.query(Task).filter_by(state=0)
count = taskgetter.count()
if count != 0:
atask = taskgetter.offset(random.randint(0, count-1)).first()
到这里一切都还不错。但是现在,我该如何确保这些记录不和另一组记录相邻呢?
Geoalchemy有一个叫做 ST_Union 的函数,可以把几何形状合并在一起,还有一个 ST_Disjoint 的函数,可以检测它们是否相交。所以看起来我应该能够选择状态为1的记录,把它们合并成一个几何形状,然后再过滤掉我最开始的查询(上面提到的),只保留那些和它不相交的记录。但我找不到在geoalchemy中表达这个的方式。这是我尝试的一种方法:
session = DBSession()
taskgetter = session.query(Task).filter_by(state=0) \
.filter(Task.geometry.ST_Disjoint(session.query( \
Task.geometry.ST_Union()).filter_by(state=1)))
count = taskgetter.count()
if count != 0:
atask = taskgetter.offset(random.randint(0, count-1)).first()
结果却出现了这样的错误:
ProgrammingError: (ProgrammingError) subquery in FROM must have an alias
LINE 3: FROM tasks, (SELECT ST_Union(tasks.geometry) AS "ST_Union_1"...
^
HINT: For example, FROM (SELECT ...) [AS] foo.
'SELECT count(*) AS count_1
FROM (SELECT tasks.id AS tasks_id
FROM tasks, (SELECT ST_Union(tasks.geometry) AS "ST_Union_1"
FROM tasks
WHERE tasks.state = %(state_1)s)
WHERE tasks.state = %(state_2)s AND ST_Disjoint(tasks.geometry, (SELECT ST_Union(tasks.geometry) AS "ST_Union_1"
FROM tasks
WHERE tasks.state = %(state_1)s))) AS anon_1' {'state_1': 1, 'state_2': 0}
1 个回答
1
这只是个猜测,因为我没有环境来测试:
这看起来跟SQLAlchemy的子查询关系更大,而不是GeoAlchemy。试着在你的子查询后面加上.subquery(),这样可以生成一个别名(参考:http://docs.sqlalchemy.org/en/rel_0_9/orm/tutorial.html#using-subqueries)
编辑:
还是根据链接里的教程,我觉得这样可能有效:
state1 = session.query(
Task.geometry.ST_Union().label('taskunion')
).filter_by(state=1).subquery()
taskgetter = session.query(Task)\
.filter_by(state=0)
.filter(Task.geometry.ST_Disjoint(state1.c.taskunion))
给你在子查询中创建的列添加一个标签,这样你就可以在主查询中引用它。