Partager via


geo_polygon_to_s2cells()

S’applique à : ✅Microsoft Fabric✅Azure Data ExplorerAzure MonitorMicrosoft Sentinel

Calcule des jetons de cellule S2 qui couvrent un polygone ou un multipolygon sur Terre. Cette fonction est un outil de jointure géospatiale utile.

En savoir plus sur la hiérarchie de cellules S2.

Syntaxe

geo_polygon_to_s2cells(polygon [, niveau[, rayon]])

En savoir plus sur les conventions de syntaxe.

Paramètres

Nom Type Requise Description
polygone dynamic ✔️ Polygone ou multipolygon au format GeoJSON.
level int Définit le niveau de cellule demandé. Les valeurs prises en charge se trouvent dans la plage [0, 30]. Si la valeur n’est pas spécifiée, la valeur par défaut 11 est utilisée.
rayon real Rayon de mémoire tampon en mètres. Si la valeur n’est pas spécifiée, la valeur par défaut 0 est utilisée.

Retours

Tableau de chaînes de jeton de cellule S2 qui couvrent un polygone ou un multipolygon. Si le rayon est défini sur une valeur positive, le revêtement sera, en plus de la forme d’entrée, de tous les points dans le rayon de la géométrie d’entrée. Si le polygone, le niveau, le rayon n’est pas valide ou si le nombre de cellules dépasse la limite, la requête produit un résultat null.

Remarque

  • Le fait de couvrir le polygone avec des jetons de cellule S2 peut être utile dans les coordonnées correspondantes aux polygones qui peuvent inclure ces coordonnées et les polygones correspondants aux polygones.
  • Les jetons de couverture de polygone sont du même niveau de cellule S2.
  • Le nombre maximal de jetons par polygone est de 65536.
  • La référence géodésique utilisée pour les mesures sur Terre est une sphère. Les arêtes de polygones sont géodésiques sur la sphère.
  • Si les bords de polygone d’entrée sont des lignes cartestériennes droites, envisagez d’utiliser geo_polygon_densify() pour convertir les bords planaires en géodésiques.

Motivation pour couvrir les polygones avec des jetons de cellule S2

Sans cette fonction, voici une approche que nous pourrions adopter pour classer les coordonnées en polygones contenant ces coordonnées.

let Polygons = 
    datatable(description:string, polygon:dynamic)
    [  
      "New York",  dynamic({"type":"Polygon","coordinates":[[[-73.85009765625,40.85744791303121],[-74.16046142578125,40.84290487729676],[-74.190673828125,40.59935608796518],[-73.83087158203125,40.61812224225511],[-73.85009765625,40.85744791303121]]]}),
      "Seattle",   dynamic({"type":"Polygon","coordinates":[[[-122.200927734375,47.68573021131587],[-122.4591064453125,47.68573021131587],[-122.4755859375,47.468949677672484],[-122.17620849609374,47.47266286861342],[-122.200927734375,47.68573021131587]]]}),
      "Las Vegas", dynamic({"type":"Polygon","coordinates":[[[-114.9,36.36],[-115.4498291015625,36.33282808737917],[-115.4498291015625,35.84453450421662],[-114.949951171875,35.902399875143615],[-114.9,36.36]]]}),
    ];
let Coordinates = 
    datatable(longitude:real, latitude:real)
    [
      real(-73.95),  real(40.75), // New York
      real(-122.3),  real(47.6),  // Seattle
      real(-115.18), real(36.16)  // Las Vegas
    ];
Polygons | extend dummy=1
| join kind=inner (Coordinates | extend dummy=1) on dummy
| where geo_point_in_polygon(longitude, latitude, polygon)
| project longitude, latitude, description

Sortie

longitude latitude description
-73,95 40,75 Ville de New York
-122.3 47.6 Seattle
-115.18 36.16 Las Vegas

Bien que cette méthode fonctionne dans certains cas, elle est inefficace. Cette méthode effectue une jointure croisée, ce qui signifie qu’elle tente de faire correspondre chaque polygone à chaque point. Ce processus consomme une grande quantité de mémoire et de ressources de calcul. Au lieu de cela, nous aimerions faire correspondre chaque polygone à un point avec une probabilité élevée de réussite de l’isolement et filtrer d’autres points.

Cette correspondance peut être obtenue par le processus suivant :

  1. Conversion de polygones en cellules S2 de niveau k,
  2. Conversion de points vers le même niveau de cellules S2 k,
  3. Jointure sur les cellules S2,
  4. Filtrage par geo_point_in_polygon(). Cette phase peut être omise si une certaine quantité de faux positifs est ok. L’erreur maximale est la zone des cellules s2 au niveau k au-delà de la limite du polygone.

Choix du niveau de cellule S2

  • Dans l’idéal, nous voulons couvrir chaque polygone avec une ou quelques cellules uniques, afin qu’aucun polygone ne partage la même cellule.
  • Si les polygones sont proches les uns des autres, choisissez le niveau de cellule S2 de sorte que son bord de cellule soit plus petit (4, 8, 12 fois plus petit) que le bord du polygone moyen.
  • Si les polygones sont éloignés les uns des autres, choisissez le niveau de cellule S2 de sorte que son bord de cellule soit similaire ou plus grand que le bord du polygone moyen.
  • En pratique, couvrir un polygone avec plus de 10 000 cellules peut ne pas produire de bonnes performances.
  • Exemples de cas d’usage :
  • Le niveau de cellule S2 5 peut s’avérer utile pour couvrir les pays/régions.
  • Le niveau de cellule S2 16 peut couvrir les quartiers denses et relativement petits de Manhattan (New York).
  • Le niveau de cellule S2 11 peut être utilisé pour couvrir les banlieues d’Australie.
  • Le temps d’exécution des requêtes et la consommation de mémoire peuvent différer considérablement en raison de différentes valeurs de niveau de cellule S2.

Avertissement

Couvrir un polygone de grande zone avec des cellules de petite zone peut entraîner une grande quantité de cellules couvrantes. Par conséquent, la requête peut retourner null.

Remarque

Suggestions d’amélioration des performances :

  • Si possible, réduisez la taille des tables de coordonnées avant la jointure, en regroupant les coordonnées qui sont très proches les unes des autres à l’aide du clustering géospatial ou en filtrant les coordonnées unnessaires en raison de la nature des données ou des besoins de l’entreprise.
  • Si possible, réduisez le nombre de polygones en raison de la nature des données ou des besoins métier. Filtrez les polygones inutiles avant la jointure, l’étendue à la zone d’intérêt ou unifier les polygones.
  • En cas de polygones très volumineux, réduisez leur taille à l’aide de geo_polygon_simplify().
  • La modification du niveau de cellule S2 peut améliorer les performances et la consommation de mémoire.
  • La modification du type de jointure et de l’indicateur peut améliorer les performances et la consommation de mémoire.
  • Si un rayon positif est défini, vous pouvez essayer d’améliorer les performances en rétablissant le rayon 0 sur la forme mise en mémoire tampon à l’aide de geo_polygon_buffer().

Exemples

L’exemple suivant classifie les coordonnées en polygones.

let Polygons = 
    datatable(description:string, polygon:dynamic)
    [
        'Greenwich Village', dynamic({"type":"Polygon","coordinates":[[[-73.991460000000131,40.731738000000206],[-73.992854491775518,40.730082566051351],[-73.996772,40.725432000000154],[-73.997634685522883,40.725786309886963],[-74.002855946639244,40.728346630056791],[-74.001413,40.731065000000207],[-73.996796995070824,40.73736378205173],[-73.991724524037934,40.735245208931886],[-73.990703782359589,40.734781896080477],[-73.991460000000131,40.731738000000206]]]}),
        'Upper West Side',   dynamic({"type":"Polygon","coordinates":[[[-73.958357552055688,40.800369095633819],[-73.98143901556422,40.768762584141953],[-73.981548752788598,40.7685590292784],[-73.981565335901905,40.768307084720796],[-73.981754418060945,40.768399727738668],[-73.982038573548124,40.768387823012056],[-73.982268248204349,40.768298621883247],[-73.982384797518051,40.768097213086911],[-73.982320919746599,40.767894461792181],[-73.982155532845766,40.767756204474757],[-73.98238873834039,40.767411004834273],[-73.993650353659021,40.772145571634361],[-73.99415893763998,40.772493009137818],[-73.993831082030937,40.772931787850908],[-73.993891252437052,40.772955194876722],[-73.993962585514595,40.772944653908901],[-73.99401262480508,40.772882846631894],[-73.994122058082397,40.77292405902601],[-73.994136652588594,40.772901870174394],[-73.994301342391154,40.772970028663913],[-73.994281535134448,40.77299380206933],[-73.994376552751078,40.77303955110149],[-73.994294029824005,40.773156243992048],[-73.995023275860802,40.773481196576356],[-73.99508939189289,40.773388475039134],[-73.995013963716758,40.773358035426909],[-73.995050284699261,40.773297153189958],[-73.996240651898916,40.773789791397689],[-73.996195837470992,40.773852356184044],[-73.996098807369748,40.773951805299085],[-73.996179459973888,40.773986954351571],[-73.996095245226442,40.774086186437756],[-73.995572265161172,40.773870731394297],[-73.994017424135961,40.77321375261053],[-73.993935876811335,40.773179512586211],[-73.993861942928888,40.773269531698837],[-73.993822393527211,40.773381758622882],[-73.993767019318497,40.773483981224835],[-73.993698463744295,40.773562141052594],[-73.993358326468751,40.773926888327956],[-73.992622663865575,40.774974056037109],[-73.992577842766124,40.774956016359418],[-73.992527743951555,40.775002110439829],[-73.992469745815342,40.775024159551755],[-73.992403837191887,40.775018140390664],[-73.99226708903538,40.775116033858794],[-73.99217809026365,40.775279293897171],[-73.992059084937338,40.775497598192516],[-73.992125372394938,40.775509075053385],[-73.992226867797001,40.775482211026116],[-73.992329346608813,40.775468900958522],[-73.992361756801131,40.775501899766638],[-73.992386042960277,40.775557180424634],[-73.992087684712729,40.775983970821372],[-73.990927174149746,40.777566878763238],[-73.99039616003671,40.777585065679204],[-73.989461267506471,40.778875124584417],[-73.989175778438053,40.779287524015778],[-73.988868617400072,40.779692922911607],[-73.988871874499793,40.779713738253008],[-73.989219022880576,40.779697895209402],[-73.98927785904425,40.779723439271038],[-73.989409054180143,40.779737706471963],[-73.989498614927044,40.779725044389757],[-73.989596493388234,40.779698146683387],[-73.989679812902509,40.779677568658038],[-73.989752702937935,40.779671244211556],[-73.989842247806507,40.779680752670664],[-73.990040102120489,40.779707677698219],[-73.990137977524839,40.779699769704784],[-73.99033584033225,40.779661794394983],[-73.990430598697046,40.779664973055503],[-73.990622199396725,40.779676064914298],[-73.990745069505479,40.779671328184051],[-73.990872114282197,40.779646007643876],[-73.990961672224358,40.779639683751753],[-73.991057472829539,40.779652352625774],[-73.991157429497036,40.779669775606465],[-73.991242817404469,40.779671367084504],[-73.991255318289745,40.779650782516491],[-73.991294887120119,40.779630209208889],[-73.991321967649895,40.779631796041372],[-73.991359455569423,40.779585883337383],[-73.991551059227476,40.779574821437407],[-73.99141982585985,40.779755280287233],[-73.988886144117032,40.779878898532999],[-73.988939656706265,40.779956178440393],[-73.988926103530844,40.780059292013632],[-73.988911680264692,40.780096037146606],[-73.988919261468567,40.780226094343945],[-73.988381050202634,40.780981074045783],[-73.988232413846987,40.781233144215555],[-73.988210420831663,40.781225482542055],[-73.988140000000143,40.781409000000224],[-73.988041288067166,40.781585961353777],[-73.98810029382463,40.781602878305286],[-73.988076449145055,40.781650935001608],[-73.988018059972219,40.781634188810422],[-73.987960792842145,40.781770987031535],[-73.985465811970457,40.785360700575431],[-73.986172704965611,40.786068452258647],[-73.986455862401996,40.785919219081421],[-73.987072345615601,40.785189638820121],[-73.98711901394276,40.785210319004058],[-73.986497781023601,40.785951202887254],[-73.986164628806279,40.786121882448327],[-73.986128422486075,40.786239001331111],[-73.986071135219746,40.786240706026611],[-73.986027274789123,40.786228964236727],[-73.986097637849426,40.78605822569795],[-73.985429321269592,40.785413942184597],[-73.985081137732209,40.785921935110366],[-73.985198833254501,40.785966552197777],[-73.985170502389906,40.78601333415817],[-73.985216218673656,40.786030501816427],[-73.98525509797993,40.785976205511588],[-73.98524273937646,40.785972572653328],[-73.98524962933017,40.785963139855845],[-73.985281779186749,40.785978620950075],[-73.985240032884533,40.786035858136792],[-73.985683885242182,40.786222123919686],[-73.985717529004575,40.786175994668795],[-73.985765660297687,40.786196274858618],[-73.985682871922691,40.786309786213067],[-73.985636270930442,40.786290150649279],[-73.985670722564691,40.786242911993817],[-73.98520511880038,40.786047669212785],[-73.985211035607492,40.786039554883686],[-73.985162639946992,40.786020999769754],[-73.985131636312062,40.786060297019972],[-73.985016964065125,40.78601423719563],[-73.984655078830457,40.786534741807841],[-73.985743787901043,40.786570082854738],[-73.98589227228328,40.786426529019593],[-73.985942854994988,40.786452847880334],[-73.985949561556794,40.78648711396653],[-73.985812373526713,40.786616865357047],[-73.985135209703174,40.78658761889551],[-73.984619428584324,40.786586016349787],[-73.981952458164173,40.790393724337193],[-73.972823037363767,40.803428052816756],[-73.971036786332192,40.805918478839672],[-73.966701,40.804169000000186],[-73.959647,40.801156000000113],[-73.958508540159471,40.800682279767472],[-73.95853274080838,40.800491362464697],[-73.958357552055688,40.800369095633819]]]}),
        'Upper East Side',   dynamic({"type":"Polygon","coordinates":[[[-73.943592454622546,40.782747908206574],[-73.943648235390199,40.782656161333449],[-73.943870759887162,40.781273026571704],[-73.94345932494096,40.780048275653243],[-73.943213862652243,40.779317588660199],[-73.943004239504688,40.779639495474292],[-73.942716005450905,40.779544169476175],[-73.942712374762181,40.779214856940001],[-73.942535563208608,40.779090956062532],[-73.942893408188027,40.778614093246276],[-73.942438481745029,40.777315235766039],[-73.942244919522594,40.777104088947254],[-73.942074188038887,40.776917846977142],[-73.942002667222781,40.776185317382648],[-73.942620205199006,40.775180871576474],[-73.94285645694552,40.774796600349191],[-73.94293043781397,40.774676268036011],[-73.945870899588215,40.771692257932997],[-73.946618690150586,40.77093339256956],[-73.948664164778933,40.768857624399587],[-73.950069793030679,40.767025088383498],[-73.954418260786071,40.762184104951245],[-73.95650786241211,40.760285256574043],[-73.958787773424007,40.758213471309809],[-73.973015157270069,40.764278692864671],[-73.955760332998182,40.787906554459667],[-73.944023,40.782960000000301],[-73.943592454622546,40.782747908206574]]]}),
    ];
let Coordinates = 
    datatable(longitude:real, latitude:real)
    [
        real(-73.9741), 40.7914, // Upper West Side
        real(-73.9950), 40.7340, // Greenwich Village
        real(-73.9584), 40.7688, // Upper East Side
    ];
let Level = 16;
Polygons
| extend covering = geo_polygon_to_s2cells(polygon, Level) // cover every polygon with s2 cell token array
| mv-expand covering to typeof(string)                     // expand cells array such that every row will have one cell mapped to its polygon
| join kind=inner hint.strategy=broadcast                  // assume that Polygons count is small (In some specific case)
(
    Coordinates
    | extend covering = geo_point_to_s2cell(longitude, latitude, Level) // cover point with cell
) on covering // join on the cell, this filters out rows of point and polygons where the point definitely does not belong to the polygon
| where geo_point_in_polygon(longitude, latitude, polygon) // final filtering for exact result
| project longitude, latitude, description

Sortie

longitude latitude description
-73.9741 40.7914 Upper West Side
-73.995 40.734 Greenwich Village
-73.9584 40.7688 Upper East Side

Voici encore plus d’amélioration sur la requête ci-dessus. Compter les événements de tempête par état américain. La requête ci-dessous effectue une jointure très efficace, car elle ne transporte pas de polygones par le biais de la jointure et utilise l’opérateur de recherche

let Level = 6;
let polygons = materialize(
    US_States
    | project StateName = tostring(features.properties.NAME), polygon = features.geometry, id = new_guid());
let tmp = 
    polygons
    | project id, covering = geo_polygon_to_s2cells(polygon, Level) 
    | mv-expand covering to typeof(string)
    | join kind=inner hint.strategy=broadcast
            (
                StormEvents
                | project lng = BeginLon, lat = BeginLat
                | project lng, lat, covering = geo_point_to_s2cell(lng, lat, Level)
            ) on covering
    | project-away covering, covering1;
tmp | lookup polygons on id
| project-away id
| where geo_point_in_polygon(lng, lat, polygon)
| summarize StormEventsCountByState = count() by StateName

Sortie

StateName StormEventsCountByState
Floride 960
Géorgie 1085
... ...

L’exemple suivant filtre les polygones qui ne se croisent pas avec la zone du polygone d’intérêt. L’erreur maximale est diagonale de longueur de s2cell. Cet exemple est basé sur une terre polygonée au format raster nocturne.

let intersection_level_hint = 7;
let area_of_interest = dynamic({"type": "Polygon","coordinates": [[[-73.94966125488281,40.79698248639272],[-73.95841598510742,40.800426144169315],[-73.98124694824219,40.76806170936614],[-73.97283554077148,40.7645513650551],[-73.94966125488281,40.79698248639272]]]});
let area_of_interest_covering = geo_polygon_to_s2cells(area_of_interest, intersection_level_hint);
EarthAtNight
| project value = features.properties.DN, polygon = features.geometry
| extend covering = geo_polygon_to_s2cells(polygon, intersection_level_hint)
| mv-apply c = covering to typeof(string) on
(
    summarize is_intersects = take_anyif(1, array_index_of(area_of_interest_covering, c) != -1)
)
| where is_intersects == 1
| count

Sortie

Count
83

Nombre de cellules qui seront nécessaires pour couvrir certains polygones avec des cellules S2 de niveau 5.

let polygon = dynamic({"type":"Polygon","coordinates":[[[0,0],[0,50],[100,50],[0,0]]]});
print s2_cell_token_count = array_length(geo_polygon_to_s2cells(polygon, 5));

Sortie

s2_cell_token_count
286

La couverture d’un polygone de grande zone avec des cellules de petite zone retourne null.

let polygon = dynamic({"type":"Polygon","coordinates":[[[0,0],[0,50],[100,50],[0,0]]]});
print geo_polygon_to_s2cells(polygon, 30);

Sortie

print_0

La couverture d’un polygone de grande zone avec des cellules de petite zone retourne null.

let polygon = dynamic({"type":"Polygon","coordinates":[[[0,0],[0,50],[100,50],[0,0]]]});
print isnull(geo_polygon_to_s2cells(polygon, 30));

Sortie

print_0
1