J'ai 2 jeux de données comprenant des données de parcelles cadastrales - environ 125 000 lignes chacune. La colonne de géométrie est constituée de polygones WKB représentant les limites des parcelles; toutes les données sont géométriquement valides (les polygones sont fermés, etc.).
Certaines données récentes sont arrivées dans une projection différente des données de base utilisées pour un travail de comparaison - j'ai donc reprojeté la plus récente (la base était 4326; l'autre était WGA94 qui a été importé dans PostGIS sous le numéro 900914 ... Je l'ai reprojeté en 4326) .
La première étape de l'analyse a consisté à rechercher et à stocker des colis non correspondants; cela consiste en partie à identifier et à stocker des colis de géométrie identique.
J'ai donc exécuté une requête très standard (le bloc de code ci-dessous résume les détails du schéma, etc.):
create table matchdata as
select a.*
from gg2014 a, gg2013 b
where ST_Equals(a.g1,b.g1)
Résultats ZÉRO.
"Bizarre ..." pensai-je. "Peut-être qu'il y a eu de minuscules changements de vertex provoqués par la reprojection: ce serait ennuyeux et ne devrait vraiment pas se produire."
Heureusement, il existe d'abondantes données aspatiales (5 colonnes identifiantes) qui me permettent d'établir des parcelles qui devraient être spatialement identiques: celles avec le même identifiant, dont la date de changement dans le tableau 2014 était antérieure à la date de changement max dans les données 2013. Cela représentait 120 086 lignes distinctes.
J'ai stocké les identifiants et les géométries dans une table distincte ( match_id
) et j'ai exécuté la requête suivante:
select apid,
bpid,
ST_Area(ag::geometry) as aa,
ST_Area(bg::geometry) as ab,
ST_Area(ST_Intersection(ag,bg)::geometry)/ST_Area(ag::geometry) as inta,
ST_Area(ST_Intersection(ag,bg)::geometry)/ST_Area(ag::geometry) as intb
from match_id
order by inta
Les 16 premières valeurs pour inta
et intb
étaient identiques à zéro, les 456 suivantes étaient de 0,99999999-ish (min 0,99999999999994, max 0,99999999999999999) et les lignes 473 à partir de 1 - jusqu'à la ligne 120050, lorsque la zone de l'intersection était supérieure à l'une ou l'autre géométrie (la plus grande valeur inta
et intb
était 1,00000000000029, mais quand même).
Voici donc mon énigme: si deux géométries se croisent spatialement entre 99,999999999994% et 100,000000000029% de leurs zones respectives, je voudrais que "ST_Equals" dise "Oui ... je vais vous donner celle-là. Assez près".
Après tout, cela équivaut à une perte d'environ 1 partie sur 16 billions ... c'est-à-dire, comme si la dette nationale américaine avait diminué de moins de 93 cents.
Dans le contexte de la circonférence de la Terre (à environ 40000 km), c'est comme être décalé de 0,000000000025 km, au sommet (car pour aboutir à une différence de surface aussi petite, tout changement de sommet doit être encore plus petit).
Selon TFD (que j'ai R'd), la tolérance ST_Intersects()
est théoriquement de 0,00001 m (1 mm), donc les changements implicites dans les sommets (que j'avoue que je n'ai pas vérifiés: je les ferai ST_Dump()
et le ferai) sembleraient plus petits que la tolérance. (Je m'en rends compte ST_Intersects !== ST_Intersection()
, mais c'est la seule tolérance mentionnée).
Je n'ai pas pu trouver la tolérance correspondante pour la comparaison de sommets entreprise par ST_Equals()
... mais il semble vraiment étrange qu'au moins 120 000 de mes rangées devraient passer une évaluation raisonnable de l'identité spatiale, mais ne le font pas.
(Remarque: j'ai également fait le même exercice en utilisant ::geography
- avec des résultats qui avaient plus de variabilité, mais toujours plus de 110 000 entrées avec un joli «1» propre).
Existe-t-il un moyen de desserrer la tolérance de ST_Equals, qui ne nécessite pas de creuser dans les interstices du code? Je ne veux pas faire ça.
Sinon, y a-t-il un soupçon dont quelqu'un est au courant?
Remarque: ce serait bien si le «kludge» ne faisait pas une comparaison bilatérale comme
where ST_within(g1, ST_Buffer(g2, 0.0000001))
and ST_within(g2, ST_Buffer(g1, 0.0000001))
- I've done that: sure, it works... but it's a gigantic documentation PITA).
Je peux contourner cela, mais écrire les 20 pages pour documenter la solution de contournement - qui ne reviendra jamais que si nous obtenons des données douteuses - est un PITA que je préférerais ne pas avoir à faire étant donné qu'il est probable qu'il s'agisse d'une opération ponctuelle .
(Versions: Postgresql 9.3.5; PostGIS 2.1.3)
ST_Equals
renvoie uniquementtrue
lorsque les géométries sont égales - type de géométrie, nombre de sommets, SRID et valeurs des sommets (dans toutes les dimensions, dans le même ordre). S'il existe un écart, la comparaison s'arrête etfalse
est renvoyée.ST_Equals()
ignore la directionnalité. J'ai pris cela pour signifier que pour un polygone 2D fermé, cela ne fait aucune différence si les points sont énumérés dans le sens des aiguilles d'une montre contre dans le sens inverse des aiguilles d'une montre.ST_OrderingEquals()
est le test le plus serré. Cela dit, après avoir inspecté les sommets (en utilisantST_Dump()
et en calculant les deltas pour chaque sommet), il est clair que la réponse impressionnante de @John Barça est sur l'argent.ST_equals()
est contre-indiqué, même pour des données ex ante identiques connues, si une géométrie est reprojetée - sauf si la comparaison est effectuée avec ST_SnapToGrid ().(100*(ST_Area(ST_Intersection(a.g1, b.g1))/ST_Area(a.g1)))::int as int_pca
et(100*(ST_Area(ST_Intersection(a.g1, b.g1))/ST_Area(b.g1)))::int as int_pcb
(assurez-vous que votreJOIN
comprendST_Intersects(a.g1,b.g1)
). Testez si(int_pca, int_pcb)=(100,100)
(ou un autre ensemble de seuils). Kludgy, mais cela fera 2,6 millions de colis en ~ 30 minutes (tant que g1 est indexé GIST).Réponses:
Je suppose que vous coordonnez des transformations qui ont introduit de minuscules erreurs d'arrondi (voir un exemple ci-dessous). Comme il n'y a aucun moyen de définir la tolérance dans ST_Equals, cela provoque ST_Equals à retourner faux pour certaines géométries qui ne diffèrent que par la nième décimale, car les géométries doivent être identiques à tous égards - voir la définition de la matrice d'intersection dans les libges. . Vous pouvez vérifier cela avec un exemple vraiment extrême,
qui retourne faux .
Si vous utilisez ST_SnapToGrid, vous pouvez imposer une précision donnée, par exemple, à dix décimales,
renvoie maintenant vrai .
Si vous deviez courir,
fixer une tolérance appropriée, je soupçonne que vos problèmes disparaîtraient.
Voici un lien vers une discussion des développeurs de Postgis sur la tolérance qui suggère qu'elle est moins banale à mettre en œuvre.
J'ai fait quelques conversions entre British National Grid (EPSG: 27700) et lat / lon pour illustrer le point sur la précision de l'arrondi, en prenant un point quelque part à Londres,
Retour
POINT(-0.19680497282746 51.5949871603888)
et inverser cela,
Retour
POINT(525000.000880007 189999.999516211)
qui est décalé de moins d'un millimètre, mais plus que suffisant pour que ST_Equals retourne faux.
la source
Avez-vous exécuté la vérification ST_IsValid sur vos géométries? S'ils ne sont pas valides, tous les paris sont désactivés. ST_Intersects et l'autre famille de fonctions de relations spatiales GEOS retournent souvent juste faux car la zone n'est pas bien définie du point de vue de la matrice d'intersection. La raison pour laquelle ST_Buffer fonctionne probablement est qu'il convertit vos géométries invalides en géométries valides. ST_Buffer (..., tinybit) est ce que l'on appelle un outil "pauvre essayant de rendre ma géométrie valide".
la source
ST_isValid(g1)
- comme cela a été mentionné (obliquement) "[la] colonne de géométrie est des polygones WKB représentant les limites des parcelles; toutes les données sont géométriquement valides (les polygones sont fermés, etc.) ."Ma réponse arrive un peu tard, mais cela aidera peut-être quelqu'un qui a le même problème. D'après mon expérience, lorsque deux géométries sont effectivement égales mais que ST_Equals renvoie Faux, deux choses peuvent aider:
ST_Equals(st_astext(a.geom), st_astext(b.geom))
au lieu deST_Equals(a.geom , b.geom)
Le premier est déjà mentionné dans la documentation . Le second semble irrationnel mais fonctionne. Je ne sais pas, mais je suppose que cela a à voir avec le format binaire de la géométrie postGIS par défaut.
la source