Запрос PostGIS Geometry возвращает «Ошибка: операция со смешанной геометрией SRID» только для определенных значений

17

У меня есть таблица PostGIS с двумя столбцами геометрии, оба определены в SRID 4326. Я могу без проблем вставить в таблицу следующий INSERTоператор (где lngи latзначения передаются программно):

INSERT INTO pad_meta (
    uuid, created, updated, name, origin, radius, area, expiry, creator
) VALUES (
    $1, now(), now(), $2, ST_GeomFromText('POINT(lng, lat)', 4326), $3, 
    ST_Buffer(ST_GeomFromText('POINT(lng, lat)', 4326), $4), $5, $6
)

Но когда я запрашиваю пересечение, используя ST_Intersects, в зависимости от значения точки, которую я получаю ERROR: Operation on mixed SRID geometries.

Например, этот запрос работает:

SELECT * FROM pad_meta where ST_Intersects(
    'POINT(-122.334172173172 46.602634395263560)'::geometry, area::geometry 
) ORDER BY created DESC;

И это ошибки:

SELECT * FROM pad_meta where ST_Intersects(
    'POINT(-122.334172173172 47.602634395263560)'::geometry, area::geometry 
) ORDER BY created DESC;

Обратите внимание, что это идентичные запросы, кроме значения долготы. Я экспериментировал с разными значениями, но не определил четкую точку перехода между запросами, которые работают и не работают.

Я думаю, что я в корне неправильно понимаю что-то. На данный момент я решил / исправил / обошел проблему, переформатировав запрос ST_GeomFromTextи явно указав SRID:

SELECT * FROM pad_meta where ST_Intersects(
    ST_GeomFromText('POINT(-122.334172173172 47.602634395263560)', 4326), area
) ORDER BY created DESC;

Но я, честно говоря, не очень понимаю, в чем разница, или действительно ли это «решение».

Мой вопрос: почему я получаю сообщение об ошибке только для определенных значений, и как правильно отформатировать этот запрос?

Вот мое определение таблицы для справки:

CREATE TABLE IF NOT EXISTS pad_meta (
  uuid CHAR(32),
  created TIMESTAMP,
  updated TIMESTAMP,
  name VARCHAR(128),
  origin GEOMETRY(Point, 4326),
  radius INTEGER,
  area GEOMETRY(Polygon, 4326),
  expiry TIMESTAMP,
  creator CHAR(32),
  PRIMARY KEY (uuid)
);

Я также проверил, что в geometry_columns есть только один тип SRID:

SELECT f_table_name, f_geometry_column, srid FROM geometry_columns;
f_table_name | f_geometry_column | srid
--------------+-------------------+------
 pad_meta     | origin            | 4326
 pad_meta     | area              | 4326

Помощь / совет приветствуется. Спасибо! (Примечание: я также видел этот вопрос , но, поскольку я уже явно определяю свои геометрические SRID при вставке в таблицу, кажется, что это не то, что происходит.)

jessykate
источник

Ответы:

24

Когда вы указываете геометрию без SRID, это фактически 0(или -1для версии <2):

SELECT ST_SRID('POINT(-122.334172173172 46.602634395263560)'::geometry);
 st_srid
---------
       0

Поэтому, когда вы используете эту геометрию с другой с SRID = 4326, она смешивается 0и 4326. Обычно это полезная ошибка, если пространственные привязки действительно разные. В вашем случае SRID одинаковы, но вы не кодировали SRID в точку запроса. Поэтому для исправления запроса всегда указывайте один и тот же SRID для точки запроса , и они больше не будут смешиваться.

В качестве примечания, тип имеет SRID по умолчанию 4326 (WGS 84), как показано здесь:geography

SELECT ST_SRID('POINT(-122.334172173172 46.602634395263560)'::geography::geometry);
 st_srid
---------
    4326

Таким образом, если вы используете geographyтипы вместо geometryтипов, SRID указывать не нужно (если только вы не хотите использовать другой SRID для альтернативного эллипсоида для Марса или чего-либо еще).


Что касается того, почему в одном запросе есть ошибка, а в другом нет, ST_Intersectsсначала выполняется &&поиск в ограничивающей рамке, который выполняется быстро и не заботится о SRID. Смешанные сообщения об ошибках SRID не будут выдаваться, если ограничивающие рамки не пересекаются. Но если они пересекаются, второй фильтр _ST_Intersects, который является более точным, проверяет два SRID, чтобы убедиться, что они совпадают, и выдает ошибку, если они смешаны. Например:

WITH pad_meta AS (
    SELECT 'SRID=4326;POLYGON((-124 50, -124 47, -121 50, -124 50))'::geometry AS area)

SELECT * FROM pad_meta where ST_Intersects(
    'POINT(-122.334172173172 46.602634395263560)'::geometry, area::geometry 
);

не имеет никаких пересекающихся ограничительных рамок и обходов _ST_Intersects. Но POINT(-122.334172173172 47.602634395263560)вызовет ошибку, потому что ограничивающие рамки перекрываются (даже если геометрия фактически не пересекается).

Однако с одинаковой геометрией и другим фильтром:

WITH pad_meta AS (
    SELECT 'SRID=4326;POLYGON((-124 50, -124 47, -121 50, -124 50))'::geometry AS area)

SELECT * FROM pad_meta where _ST_Intersects(
    'POINT(-122.334172173172 46.602634395263560)'::geometry, area::geometry 
);

выдает смешанную ошибку SRID, поскольку ограничивающие рамки не учитываются.

Майк Т
источник
вау, спасибо это имеет смысл. SRID=4326правильно ли ставить перед запросом (как вы сделали выше) правильный способ установить SRID для остальной части оператора? (в отличие от использования ST_GeomFromTextпросто потому, что я не знал, как еще указать SRID ...?) Есть ли способ установить SRID по умолчанию для запросов? кажется довольно многословным, чтобы явно установить его каждый раз. еще раз спасибо!
Джессикате
1
Я обновил свой ответ, предложив использовать geographyтипы, которые всегда равны 4326. Кроме того, есть несколько способов указать SRID.
Майк Т
0

Пара наблюдений, которые могут помочь: во-первых, Point(Double, Double)это встроенная функция PostgreSQL, которую вы приводите к типу данных PostGIS. ST_MakePoint(double x, double y)создаст правильную геометрию. Кроме того, в своем вопросе вы, похоже, называете второй аргумент долготой. Правильный порядок x, y, который соответствует Longitude, Latitude. Получение тех, кто перевернут, может привести к неожиданным результатам без каких-либо исключений.

Ничто из этого не объясняет, почему ваш первый пример иногда работает, а другие нет, но, надеюсь, это поможет вам создать хорошие запросы.

SCRo
источник