Problème pour obtenir des zips dans le rayon via MySQL


9

J'ai un tableau des codes postaux qui comprend le centre lat, lng pour chaque code postal. Je l'utilise pour obtenir une liste de codes postaux dans un rayon de mile donné à partir de n'importe quel point arbitraire.

Je viens de penser que le fait que le point central d'un zip ne se trouve pas dans un rayon donné ne signifie pas que le zip lui-même ne se trouve pas dans le rayon.

J'ai utilisé mes compétences artistiques super avancées pour illustrer le point ici:

entrez la description de l'image ici

  • Les taches à rayures vertes représentent les codes postaux A, B et C.

  • Les taches rouges sont les centres géographiques de chaque code postal

  • Le point fuchsia est l'emplacement cible, et ..

  • Le cercle bleu bosselé est un rayon de 1 mile de l'emplacement cible

Si j'exécute une requête pour tous les codes postaux dans un rayon de 1 mile à partir du maculage rose, seuls les codes postaux B et C seront retournés car le point central du zip A n'est pas dans le rayon d'un mile, même si le maculage rose lui-même est clairement dans le code postal A.

SELECT *,
        p.distance_unit
                 * DEGREES(ACOS(COS(RADIANS(p.latpoint))
                 * COS(RADIANS(z.y))
                 * COS(RADIANS(p.longpoint) - RADIANS(z.x))
                 + SIN(RADIANS(p.latpoint))
                 * SIN(RADIANS(z.y)))) AS dist
  FROM standard_zip AS z
  JOIN (   /* these are the query parameters */
        SELECT  $lat  AS latpoint,  $lng AS longpoint,
                $miles AS radius,      69 AS distance_unit
    ) AS p ON 1=1
  WHERE z.y
     BETWEEN p.latpoint  - (p.radius / p.distance_unit)
         AND p.latpoint  + (p.radius / p.distance_unit)
    AND z.x
     BETWEEN p.longpoint - (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
         AND p.longpoint + (p.radius / (p.distance_unit * COS(RADIANS(p.latpoint))))
  ORDER BY dist

Comment diable puis-je écrire une requête qui inclura le zip A dans les résultats?

J'ai accès à l'espace / géométrie pour chaque code postal que je peux ajouter au tableau si nécessaire, mais je n'ai aucune idée de la façon dont je l'utiliserais à cette fin dans MySQL.


Edit : J'ai passé une journée à lire les documents Oracle et MySQL pour les données spatiales et j'ai réussi à convertir mes données spatiales en MySQL . Comment puis-je écrire une requête similaire qui utilise la colonne de géométrie au lieu du lat et du long? J'utilise des données 2D .. la géométrie est des polygones et des multipolygones uniquement ..

Je pense que je l'ai en quelque sorte compris ..

select
  *
from
  (
    select
      MIN(st_distance(geom, POINT(-82.765136, 28.0914015))) * 69 as miles,
      zip
    from
      zip_spatial
    group by
      zip
    order by
      miles asc
  ) d
where
  d.miles < 5

Je vais laisser la prime ouverte pour le moment au cas où quelqu'un aurait une solution meilleure et plus efficace.

Réponses:


7

À partir de l' indexation et de la requête de données spatiales dans Oracle dans le manuel Oracle® Spatial Developer's Guide 11g version 2 (11.2):

Interrogation de données spatiales

Spatial utilise un modèle de requête à deux niveaux avec des opérations de filtrage principal et secondaire pour résoudre les requêtes spatiales et les jointures spatiales. Le terme à deux niveaux indique que deux opérations distinctes sont effectuées pour résoudre les requêtes. Si les deux opérations sont effectuées, l'ensemble de résultats exact est renvoyé.

Vous ne pouvez pas ajouter un nom de lien de base de données (dblink) au nom d'une table spatiale dans une requête si un index spatial est défini sur cette table.

Requête spatiale

Dans un index spatial R-tree, chaque géométrie est représentée par son rectangle de délimitation minimum (MBR). Considérez le calque suivant contenant plusieurs objets dans la figure 1. Chaque objet est étiqueté avec son nom de géométrie (geom_1 pour la chaîne de lignes, geom_2 pour le polygone à quatre côtés, geom_3 pour le polygone triangulaire et geom_4 pour l'ellipse), et le MBR autour de chaque objet est représenté par une ligne en pointillés.

Figure 1 Géométries avec MBR

Description de "Figure1 Géométries avec MBR"

Une requête spatiale typique consiste à demander tous les objets se trouvant dans une fenêtre de requête, c'est-à-dire une clôture ou une fenêtre définie. Une fenêtre de requête dynamique fait référence à une zone rectangulaire qui n'est pas définie dans la base de données, mais qui doit être définie avant d'être utilisée. La figure 2 montre les mêmes géométries que dans la figure 1, mais ajoute une fenêtre de requête représentée par la lourde boîte en pointillés.

Figure 2 Calque avec une fenêtre de requête

Description de «Figure2 couche avec une fenêtre de requête»

Dans la figure 2, la fenêtre de requête couvre des parties des géométries geom_1 et geom_2, ainsi qu'une partie du MBR pour geom_3 mais aucune de la géométrie réelle geom_3. La fenêtre de requête ne couvre aucune partie de la géométrie geom_4 ou son MBR.

Opérateur de filtre principal

L'opérateur SDO_FILTER implémente la partie de filtre principal du processus en deux étapes impliqué dans le modèle de traitement des requêtes Oracle Spatial. Le filtre principal utilise les données d'index pour déterminer uniquement si un ensemble de paires d'objets candidats peut interagir. Plus précisément, le filtre principal vérifie si les MBR des objets candidats interagissent, pas si les objets eux-mêmes interagissent. La syntaxe de l'opérateur SDO_FILTER est la suivante:

SDO_FILTER(geometry1 SDO_GEOMETRY, geometry2 SDO_GEOMETRY, param VARCHAR2)

Dans la syntaxe précédente:

  • geometry1 est une colonne de type SDO_GEOMETRY dans une table. Cette colonne doit être indexée spatialement.

  • geometry2 est un objet de type SDO_GEOMETRY. Cet objet peut ou non provenir d'une table. S'il provient d'une table, il peut ou non être indexé spatialement.

  • param est une chaîne facultative de type VARCHAR2. Il peut spécifier l'un ou les deux mots clés min_resolution et max_resolution.

Les exemples suivants effectuent uniquement une opération de filtrage principal (sans opération de filtrage secondaire). Ils renverront toutes les géométries illustrées dans la figure 2 qui ont un MBR qui interagit avec la fenêtre de requête. Le résultat des exemples suivants sont les géométries geom_1, geom_2 et geom_3.

Exemple1 effectue une opération de filtrage primaire sans insérer la fenêtre de requête dans une table. La fenêtre sera indexée en mémoire et les performances seront très bonnes.

Exemple 1 Filtre principal avec une fenêtre de requête temporaire

SELECT A.Feature_ID FROM TARGET A  WHERE sdo_filter(A.shape, SDO_geometry(2003,NULL,NULL,
                                       SDO_elem_info_array(1,1003,3),
                                       SDO_ordinate_array(x1,y1, x2,y2))
                           ) = 'TRUE';   

Dans l'exemple 1, (x1, y1) et (x2, y2) sont les coins inférieur gauche et supérieur droit de la fenêtre de requête.


1
Cool .. Je devrais donc créer la géométrie du cercle pour représenter le rayon et puis juste voir quels polygones se croisent .. intéressant .. thx pour l'info
J'ai lutté un ours une fois.

Ouais ... continue ... J'espère que ça marche bien pour toi.
l.lijith

5

Toute tentative d'inclure A inclura probablement D, E, F, G. Le problème ne peut pas être résolu sans avoir un chemin exact définissant chaque zone de code postal.

Trouvez une telle base de données, puis créez un SPATIALindex en utilisant de tels polygones arbitraires.


Je sais que j'ai besoin de données spatiales (et je les ai, mais elles se trouvent dans une table Oracle et je ne trouve pas beaucoup d'informations sur la façon de les convertir). Le problème est de savoir comment interroger les données.
J'ai lutté un ours une fois.

Si vous êtes satisfait des performances du nouveau code, c'est probablement le meilleur. Remarque: La requête répertorie la distance à chaque zip, il n'y a donc probablement aucun potentiel d'optimisation. (Je serai agréablement surpris si vous obtenez un meilleur code.)
Rick James

c'est en quelque sorte ce que je pense aussi. Je vous donnerai la prime avant la fin du délai et vous obtiendrez la moitié de toute façon. Je veux juste voir quelles autres réponses je pourrais obtenir en premier.
J'ai lutté un ours une fois.

3

Vous le faites mal. Tout d'abord, si possible, utilisez PostGIS - qui est le principal RDMBS avec une solution spatiale.

Ensuite, vous souhaitez suivre ces étapes.

  1. Déroulez les ZCTA (zones de tabulation des codes postaux) du jeu de données TIGER du recensement . Les codes postaux ne sont pas vraiment connus avec certitude. Officiellement, les codes postaux sont réservés à l'usage interne de l'USPS. Parce que tout le monde les utilise, y compris le gouvernement, la deuxième source la plus autorisée est devenue les fichiers de formes ZCTA.
  2. Importez ces fichiers de formes dans votre base de données, avec PostgreSQL vous pouvez facilement utiliser shp2pgsql
  3. Indexez la géométrie que vous avez importée.

    CREATE INDEX ON census_zcta USING gist (geog);
    ANALYZE census_zcta;
  4. Exécutez une requête de point d'intérêt (POI) sur les fichiers de formes. Le point d'intérêt dans votre cas est les cordons d'entrée, cela ressemblera à ceci,

    SELECT *
    FROM census_zcta AS zcta
      WHERE ST_Intersects( zcta, ST_MakePoint(long,lat)::geog );

9 1609,344 mètres = 1 mille

MySQL

Avec MySQL, vous aurez

  1. Utilisez ogr2ogr pour générer des instructions d'insertion MySQL pour le fichier de formes de recensement.
  2. Utilisez MBRIntersectspour utiliser l'index spatial. La requête de fin doit ressembler à quelque chose

    SELECT *
    FROM zcta
    WHERE MBRIntersects( geom, Point(long,lat) )
      AND ST_Intersects ( geom, Point(long,lat) );

3
1) je sais que je l'ai mal fait. c'est pourquoi j'ai demandé. 2) l'entreprise pour laquelle je travaille a un accès payant aux limites internes du code postal usps. nous avons travaillé directement avec les usps pour ce projet, et 3) généralement, suggérant que OP utilise un ensemble d'outils entièrement différent n'est pas une bonne réponse.
J'ai lutté un ours une fois.

1
@iwrestledabearonce Vous pouvez aussi faire tout cela avec MySQL 8 juste remplacer le ST_DWithinparMBRIntersects
Evan Carroll

1
"accès payant aux limites internes du code postal" connaissez-vous le nom de ce produit? AFAIK il n'y a rien de tel. (bien que l'USPS propose 2 produits de données et certaines API pour décoder l'adresse)
Evan Carroll

1
merci d'avoir ajouté les informations sur mysql. +1. L'API n'est pas publique et n'est répertoriée sur aucun site Web, en fait l'URL du point de terminaison n'a même pas de nom de domaine, nous le demandons directement à partir de l'adresse IP. Cependant, juste pour prouver que l'API existe, elle est répertoriée dans ce document (les 3 qui font référence à EDDM sont ceux auxquels
J'ai lutté contre un ours une fois que.

1
Cela semble réellement légitime si vous retirez le point de terminaison EDDM / SelectZIP. Ce n'est pas annoncé à cette fin, mais bravo pour trouver ce point final.
Evan Carroll du

1

Consultez cet ensemble de données de GreatData.com (notez qu'il ne s'agit pas d'un logiciel open source mais d'un service payant).

Ils utilisent la densité de population au lieu du centre du zip.

Et comment utiliser le type de données spatiales du serveur SQL pour obtenir rapidement des résultats corrects.

J'espère que cela t'aides.


Cet ensemble de données est-il disponible pour MySQL ou uniquement pour SQL Server?
ypercubeᵀᴹ
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.