GeoAlchemy2: 找到一组不与另一组相交的几何项

1 投票
1 回答
1357 浏览
提问于 2025-04-17 21:33

我有一个叫做 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))

给你在子查询中创建的列添加一个标签,这样你就可以在主查询中引用它。

撰写回答