GeoAlchemy2: найти набор элементов Geometry, которые не пересекаются с отдельным набором

У меня есть таблица базы данных postgis под названием tasks, сопоставленный с классом Python Task использование geoalchemy2/sqlalchemy - каждая запись имеет 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()

Все идет нормально. Но теперь, как убедиться, что они не соседствуют с другим набором записей?

В геоалхимии есть функция ST_Union, которая может объединять геометрии, и ST_Disjoint, которая определяет, пересекаются они или нет. Поэтому кажется, что я должен иметь возможность выбрать элементы с состоянием =1, объединить их в одну геометрию, а затем отфильтровать исходный запрос (см. Выше), чтобы сохранить только те элементы, которые не связаны с ним. Но я не могу найти способ выразить это в геоалхимии. Вот один способ, которым я попробовал:

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 ответ

Решение

Снимок в темноте, так как у меня нет настроек для его проверки:

Кажется, это связано с подзапросами SQLAlchemy больше, чем с GeoAlchemy, попробуйте добавить.subquery() в конце вашего подзапроса, чтобы сгенерировать псевдоним (cf: http://docs.sqlalchemy.org/en/rel_0_9/orm/tutorial.html)

Редактировать: Все еще используя информацию из связанного руководства, я думаю, что это может работать:

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))

Добавьте метку к столбцу, который вы создаете в своем подзапросе, чтобы сослаться на него в своем супер-запросе.

Другие вопросы по тегам