Condividi tramite


geo_polygon_to_s2cells()

Si applica a: ✅Microsoft Fabric✅Azure Esplora dati Azure MonitorMicrosoft Sentinel

Calcola i token di cella S2 che coprono un poligono o un multipolygone sulla Terra. Questa funzione è uno strumento di join geospaziale utile.

Altre informazioni sulla gerarchia di celle S2.

Sintassi

geo_polygon_to_s2cells(poligono [, level[, radius]])

Altre informazioni sulle convenzioni di sintassi.

Parametri

Nome Digita Obbligatorio Descrizione
poligono dynamic ✔️ Poligono o multipolygon nel formato GeoJSON.
level int Definisce il livello di cella richiesto. I valori supportati sono compresi nell'intervallo [0, 30]. Se non specificato, viene usato il valore predefinito 11.
raggio real Raggio del buffer in metri. Se non specificato, viene usato il valore predefinito 0.

Valori restituiti

Matrice di stringhe di token di cella S2 che coprono un poligono o un multipolygon. Se radius è impostato su un valore positivo, la copertura sarà, oltre alla forma di input, di tutti i punti all'interno del raggio della geometria di input. Se il poligono, il livello, il raggio non è valido o il numero di celle supera il limite, la query genererà un risultato Null.

Nota

  • La copertura del poligono con token di cella S2 può essere utile nella corrispondenza delle coordinate ai poligoni che possono includere queste coordinate e i poligoni corrispondenti ai poligoni.
  • Il poligono che copre i token è dello stesso livello di cella S2.
  • Il numero massimo di token per poligono è 65536.
  • Il datum geodetico usato per le misurazioni sulla Terra è una sfera. I bordi poligoni sono geodesici sulla sfera.
  • Se i bordi dei poligoni di input sono linee cartesiane dritte, è consigliabile usare geo_polygon_densify() per convertire i bordi planari in geodesici.

Motivazione per la copertura dei poligoni con token di cella S2

Senza questa funzione, ecco un approccio che è possibile adottare per classificare le coordinate in poligoni contenenti queste coordinate.

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

Output

longitude latitude description
-73,95 40,75 Città di New York
-122.3 47.6 Seattle
-115.18 36.16 Las Vegas

Anche se questo metodo funziona in alcuni casi, è inefficiente. Questo metodo esegue un cross join, ovvero tenta di associare ogni poligono a ogni punto. Questo processo utilizza una grande quantità di memoria e risorse di calcolo. Si vuole invece associare ogni poligono a un punto con un'alta probabilità di successo del contenimento e filtrare altri punti.

Questa corrispondenza può essere ottenuta tramite il processo seguente:

  1. Conversione di poligoni in celle S2 di livello k,
  2. Conversione di punti nello stesso livello di celle S2 k,
  3. Unione su celle S2,
  4. Filtro in base a geo_point_in_polygon(). Questa fase può essere omessa se una quantità di falsi positivi è ok. L'errore massimo sarà l'area delle celle s2 al livello k oltre il limite del poligono.

Scelta del livello di cella S2

  • Idealmente vorremmo coprire ogni poligono con una o solo poche celle univoche in modo che nessun poligono condividesse la stessa cella.
  • Se i poligoni sono vicini tra loro, scegliere il livello di cella S2 in modo che il bordo della cella sia minore (4, 8, 12 volte più piccolo) rispetto al bordo del poligono medio.
  • Se i poligoni sono lontani l'uno dall'altro, scegliere il livello di cella S2 in modo che il bordo della cella sarà simile o maggiore del bordo del poligono medio.
  • In pratica, coprire un poligono con più di 10.000 celle potrebbe non produrre buone prestazioni.
  • Casi d'uso di esempio:
  • Il livello di cella S2 5 potrebbe rivelarsi valido per coprire paesi/aree geografiche.
  • Il livello di cella S2 16 può coprire quartieri di Manhattan (New York) densi e relativamente piccoli.
  • Il livello di cella S2 11 può essere utilizzato per coprire i sobborghi dell'Australia.
  • Il tempo di esecuzione delle query e il consumo di memoria potrebbero variare notevolmente a causa di valori diversi a livello di cella S2.

Avviso

Coprendo un poligono di grandi aree con celle di piccole aree può portare a una grande quantità di cellule di copertura. Di conseguenza, la query potrebbe restituire null.

Nota

Suggerimenti per il miglioramento delle prestazioni:

  • Se possibile, ridurre le dimensioni della tabella prima del join, raggruppando le coordinate molto vicine tra loro usando il clustering geospaziale o filtrando le coordinate non necessarie a causa della natura dei dati o delle esigenze aziendali.
  • Se possibile, ridurre il numero di poligoni a causa della natura dei dati o delle esigenze aziendali. Filtrare i poligoni non necessari prima del join, definire l'ambito per l'area di interesse o unificare i poligoni.
  • In caso di poligoni molto grandi, ridurre le dimensioni usando geo_polygon_simplify().
  • La modifica del livello di cella S2 può migliorare le prestazioni e il consumo di memoria.
  • La modifica del tipo di join e l'hint possono migliorare le prestazioni e il consumo di memoria.
  • Se viene impostato un raggio positivo, è possibile provare a migliorare le prestazioni ripristinando il raggio 0 sulla forma memorizzata nel buffer usando geo_polygon_buffer().

Esempi

L'esempio seguente classifica le coordinate in poligoni.

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

Output

longitude latitude description
-73.9741 40.7914 Lato superiore occidentale
-73.995 40.734 Greenwich Village
-73.9584 40.7688 Upper East Side

Di seguito è riportato un miglioramento ancora maggiore sulla query precedente. Contare gli eventi storm per stato degli Stati Uniti. La query seguente esegue un join molto efficiente perché non include poligoni tramite join e usa l'operatore di ricerca

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

Output

StateName StormEventsCountByState
Florida 960
Georgia 1085
... ...

Nell'esempio seguente vengono filtrati i poligoni che non si intersecano con l'area del poligono di interesse. L'errore massimo è diagonale di lunghezza s2cell. Questo esempio si basa su una terra poligonizzata di notte.

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

Output

Count
83

Numero di celle che saranno necessarie per coprire alcuni poligoni con celle S2 di livello 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));

Output

s2_cell_token_count
286

La copertura di un poligono di grandi dimensioni con celle di area piccola restituisce Null.

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

Output

print_0

La copertura di un poligono di grandi dimensioni con celle di area piccola restituisce Null.

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

Output

print_0
1