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 uniquement true
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 et false
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 utilisant ST_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 votre JOIN
comprend ST_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).