Para ajudar a iniciar a Prévia Pública do ArcGIS GeoAnalytics for Microsoft Fabric, teremos uma série de postagens no blog explorando a funcionalidade do GeoAnalytics for Fabric. Neste post, exploraremos os fundamentos do cálculo / construção de novas geometrias, como buffers, bins espaciais e caminhos mais curtos. Também veremos como calcular propriedades como comprimento e área dessas novas geometrias.
Importando GeoAnalytics for Fabric
Começaremos com uma revisão sobre como importar o GeoAnalytics for Fabric e começar a trabalhar com ele. Para importar o GeoAnalytics for Fabric, você só precisa adicionar import geoanalytics_fabric em uma célula do seu notebook. Para mais informações sobre como habilitar a biblioteca, consulte a documentação de Introdução.
Para conveniência, recomendamos também importar as funções geoespaciais diretamente, e dar-lhes um alias fácil de usar para referência. No exemplo abaixo, importamos a biblioteca e as funções com um alias de ST. Como cada uma das funções é nomeada ST.<nome_da_função>, isso facilita a referência e identifica-as como funções do Tipo Espacial (ST). Isso também estará alinhado com a estrutura dos exemplos ao longo da documentação do ArcGIS GeoAnalytics. Sua célula ficará assim:
# import ArcGIS GeoAnalytics
import geoanalytics_fabric
import geoanalytics_fabric.sql.functions as ST
Por exemplo, após importar, você poderá referenciar a função ST_Buffer usando ST.buffer em vez de geoanalytics_fabric.sql.functions.buffer.
Calculando novas geometrias
O GeoAnalytics for Fabric contém várias operações espaciais para gerar novas geometrias com base na entrada. Por exemplo, exploraremos algumas funções comuns neste post:
Embora exploremos algumas funções em detalhes, há muitas outras na biblioteca GeoAnalytics for Fabric que podem ser interessantes para seus projetos analíticos. Você pode explorar a variedade de funções na documentação das Funções SQL.
Vamos começar com alguns buffers!
ST_Buffer & ST_GeodesicBuffer
ST_Buffer e ST_GeodesicBuffer criam polígonos representando a área menor ou igual à distância especificada ao redor da geometria de entrada (por exemplo, a região dentro de 10 milhas ao redor de um ponto). Essas funções criarão esse buffer ao redor de cada entidade na coluna de geometria.
Para ver como isso funciona, começaremos criando um novo DataFrame com algumas localizações de escolas na área de Boston. Faremos buffers nessas localizações para observar visualmente a área ao redor de cada escola. Aqui está um exemplo de criação de um novo DataFrame com algumas localizações pontuais:
# criar um DataFrame com localizações de várias escolas (em latitude e longitude)
df_schools = spark.createDataFrame([
(-71.0777, 42.3388, "Hurley K-8"),
(-71.0919,42.3176, "Higginson Elementary School"),
(-71.1016, 42.3164, "Mendell Elementary School")], ["longitude", "latitude", "name"])
# criar uma coluna de geometria pontual ("geometry") a partir dos valores de latitude e longitude
df_schools = df_schools\
.withColumn("geometry", ST.point("longitude", "latitude", sr=4326))
Aqui estão os resultados:

Agora podemos criar buffers ao redor dessas escolas.
A unidade de distância para o buffer é a mesma unidade da geometria de entrada. Por exemplo, se os dados estiverem no sistema de coordenadas Web Mercator, as unidades serão metros; se seus dados estiverem em um sistema de coordenadas geográficas, as unidades serão graus decimais.
A função ST_GeodesicBuffer pode ser usada para criar buffers usando distância geodésica em metros. Vamos criar alguns buffers e observá-los - e ver como o buffer planar e o buffer geodésico diferem.
# criar buffers de exemplo usando ST_Buffer e ST_GeodesicBuffer
# Este exemplo gera um buffer de 0.5 _graus decimais_ (as unidades para os dados de entrada são valores de latitude / longitude!)
# Também criou um buffer geodésico de 500 metros
df_schools = df_schools\
.withColumn("geometry_buffer", ST.buffer("geometry", 0.5))\
.withColumn("geometry_geodesic_buffer", ST.geodesic_buffer("geometry", 500))
Primeiro, vamos olhar o buffer planar - que foi calculado nas unidades da geometria de entrada (graus decimais). Este é um buffer de 0.5 grau decimal ao redor das três escolas:

Observe que deixei os rótulos dos eixos no mapa para que você possa ver que isso está sendo plotado em um mapa base em graus decimais.
E agora, vamos olhar os buffers geodésicos ao redor das escolas. Observe que isso também está sendo plotado em um mapa base em graus decimais. Podemos ver que os buffers são muito menores (500m ao redor de cada escola) e que não são circulares. Vamos explorar por quê...

Há algumas coisas importantes para notar ao plotar esses buffers:
- Os buffers no primeiro gráfico parecem enormes, enquanto os buffers no segundo são muito menores. O "geometry_buffer" é meio grau decimal, enquanto o "geometry_geodesic_buffer" é apenas 500 metros. Um grau decimal é significativamente maior que 500 metros. No equador, um grau de latitude ou longitude é próximo a 111 milhas!
- Os buffers no primeiro gráfico ("geometry_buffer") parecem circulares enquanto os buffers no segundo gráfico ("geometry_geodesic_buffer") parecem ovais. Por que isso? Ambos esses mapas são gerados em um mapa com base nas coordenadas de latitude e longitude. Nesses gráficos, um grau de latitude é igual a um grau de longitude em comprimento. Na Terra, o comprimento de um grau de latitude e longitude difere, com um grau de longitude tornando-se cada vez mais curto à medida que você se aproxima dos polos. A entrada da Wikipedia sobre <\/SPAN>Longitude<\/A> <\/SPAN>discute isso em detalhes interessantes se você quiser mais informações. Quando os buffers geodésicos são plotados nesta projeção de mapa em particular, eles aparecem esticados em ovais, embora, na superfície da Terra, eles seriam circulares.<\/LI><\/UL>Se fôssemos plotar o buffer geodésico em nosso sistema de coordenadas local de Boston (SRID 2249), os buffers parecem os círculos que esperaríamos.<\/P>Podemos atualizar o mapa ao plotá-lo adicionando um <\/SPAN>sr<\/FONT> <\/SPAN>(referência espacial) parâmetro. O código de exemplo abaixo demonstra isso. Mais informações sobre os parâmetros para <\/SPAN>st.plot<\/FONT> <\/SPAN>podem ser encontradas na <\/SPAN>referência da API<\/A>.<\/P>
<\/P>
# plotando o geometry_geodesic_buffer em um mapa com referência espacial 2249
df_schools\\
.st.plot(basemap="streets", geometry="geometry_geodesic_buffer", alpha=0.5, edgecolor="black", sr=2249);<\/code><\/pre><P> <\/P><P>Em geral, é mais rápido executar<SPAN> <\/SPAN><A href="https:\/\/developers.arcgis.com\/geoanalytics-fabric\/sql-functions\/st_buffer\/\ target="_blank" rel="noopener nofollow noreferrer">ST_H3Bin</A><SPAN> </SPAN>função para identificar o<SPAN> </SPAN><A href="https://h3geo.org/" target="_blank" rel="noopener nofollow noreferrer">H3 bin</A><SPAN> </SPAN>em que cada um dos nossos pontos de solicitação de serviço está dentro. Em seguida, usaremos uma função PySPark<SPAN> </SPAN><A href="https://spark.apache.org/docs/latest/api/python/reference/pyspark.sql/api/pyspark.sql.DataFrame.groupBy.html" target="_blank" rel="noopener nofollow noreferrer">GroupBy</A><SPAN> </SPAN>para criar um DataFrame simplificado que lista a contagem de pontos dentro de cada bin.</P><P>O tamanho dos bins H3 é baseado em um valor de resolução. Para este exemplo, usaremos uma resolução de 8, que equivale a um tamanho médio de bin hexagonal de 0,73 quilômetros quadrados. Mais informações sobre<SPAN> </SPAN><A href="https://h3geo.org/docs/core-library/restable" target="_blank" rel="noopener nofollow noreferrer">resoluções de bins H3 podem ser encontradas aqui</A>.</P><P>Adicionar o ID para um bin é tão fácil quanto criar uma nova coluna para os dados e usar a função bin para o tipo de geometria que você deseja (hexagonal, quadrado ou H3):</P><P> </P><pre class="lia-code-sample language-python"><code># adicionar uma coluna com o _id_ do bin H3 em que cada ponto está
df = df\
.withColumn("h3_bin", ST.h3_bin("geometry", 8))
A
função ST_H3Bin adiciona um id do bin a cada registro. Ela não adiciona a geometria especificamente, pois haveria muitas duplicatas da geometria (uma para cada ponto dentro do bin) e é mais eficiente trabalhar com o ID até precisarmos das geometrias para análise ou visualização. Podemos ver esses novos IDs de bin na tabela abaixo:
Usando o campo ID h3_bin, podemos então agrupar os valores em nossa tabela para contar os registros em cada bin. # agrupar o DataFrame de solicitações de serviço usando o ID do bin h3 e contar todos os registros
df_by_bin = df.groupBy("h3_bin").count() Após agrupar, podemos ver que cada ID de bin tem uma contagem do número de pontos dentro dele. Depois disso, podemos converter os IDs dos bins em geometrias e visualizar os resultados.
Aqui está como adicionamos a geometria do bin na tabela usando a função ST_BinGeometry: # adicionar a geometria do bin baseada no ID do bin usando ST_BinGeometry
df_by_bin = df_by_bin\
.withColumn("geometry_bin", ST.bin_geometry("h3_bin")) Plotar os resultados agora nos mostra onde ocorre a maior concentração de solicitações de serviço:
Agora vamos passar para calcular algumas linhas e distâncias entre locais!ST_ShortestLine e ST_GeodesicLineST_ShortestLine retorna uma coluna linestring representando a linha mais curta que toca duas geometrias, usando cálculo de distância planar. Esta função retorna apenas uma linha mais curta se houver mais de uma que possa ser calculada entre locais.Para criar uma linha mais curta usando cálculos de distância geodésica, use ST_GeodesicShortestLine.Como exemplo disso, vamos olhar para a linha mais curta entre o DataFrame das escolas que criamos anteriormente e as solicitações de serviço próximas. A linha mais curta usará duas colunas de geometria como entrada, então elas precisam estar no mesmo DataFrame.Nos exemplos de buffer acima, usamos ST_Contains para encontrar todos os pontos dentro do polígono buffer.Neste exemplo, usaremos outro cálculo espacial chamado ST_DWithin (distância dentro) para encontrar todos os pontos dentro de uma distância especificada dos pontos da escola. Efetivamente isso pode dar o mesmo resultado, porém DWithin é frequentemente mais performático do que criar um buffer e realizar uma junção espacial. DWithin usa cálculos planares por padrão, então devemos ou transformar a geometria em um sistema local de coordenadas, ou definir o parâmetro para usar cálculos geodésicos.Neste caso, usaremos ST_Transform para atualizar o sistema de coordenadas para este cálculo. Faremos isso em um único cálculo onde juntamos as escolas e as solicitações de serviço e criamos um novo DataFrame com os resultados: # encontrar todos os pontos dentro de 500 pés de cada escola
# usar uma junção espacial para combinar qualquer ponto que esteja dentro da distância especificada da escola
# já que os conjuntos de dados estão originalmente em coordenadas latitude/longitude, transformamos primeiro para um sistema local
df_schools_requests = df.join(df_schools.select("name", F.col("geometry").alias("geometry_school")),
ST.dwithin(ST.transform("geometry", 2249), ST.transform("geometry_school", 2249), 500)) Agora que cada escola tem a geometria da escola e os pontos próximos, podemos usar essas duas geometrias para calcular a linha mais curta entre cada ponto de solicitação de serviço e a escola. # calcular a linha mais curta de cada ponto até cada escola
df_schools_requests = df_schools_requests\
.withColumn("geometry_line", ST.shortest_line("geometry", "geometry_school")) Vamos dar uma olhada no resultado para uma escola: # plotar as linhas conectoras entre uma escola e todos os pontos dentro de 500 pés desta localização
df_schools_requests.filter(F.col("name") == "Hurley K-8").st.plot(basemap="streets", geometry="geometry_line", linewidth=0.5, alpha=0.7);
Para distâncias curtas, ST_ShortestLine é uma boa escolha, mas para distâncias maiores, você pode considerar usar ST_GeodesicShortestLine para que a linha criada represente o caminho mais curto na esfera. Como exemplo da diferença entre a "linha mais curta" (cálculo planar) e a linha geodésica, podemos explorar a diferença entre uma linha conectada entre Los Angeles e Londres. Neste exemplo, a linha geodésica (laranja) parece curva e mais longa nesta projeção, mas na verdade é a linha mais curta! # linha geodésica (geodesic) (em laranja) vs. "linha mais curta" (planar) (em azul)
# a linha geodésica parece curva e mais longa nesta projeção, mas na verdade é a linha mais curta!
# criar pontos para dois locais
data = [
("POINT (-118.2426 34.0549 )", "Los Angeles",
"POINT (0.1276 51.5072 )", "London")
]
# criar um DataFrame com as duas localizações
# converter as localizações de well-known text para geometria
df_lines = spark.createDataFrame(data, ["wkt-origin", "city-origin", "wkt-dest", "city-dest"])\
.select("city-origin",
"city-dest",
ST.geom_from_text("wkt-origin", 4326).alias("geometry-origin"),
ST.geom_from_text("wkt-dest", 4326).alias("geometry-dest"))
# plotar os resultados para comparar as duas linhas geradas (geodesic vs. shortest)
myplt = df_lines.select(ST.geodesic_shortest_line("geometry-origin", "geometry-dest")).st.plot(basemap="light", figsize=(10,10), color="orange")
df_lines.select(ST.shortest_line("geometry-origin", "geometry-dest")).st.plot(ax=myplt);
Cálculos de Distância e ComprimentoAgora vamos analisar distância e comprimento para essas linhas...ST_Distance e ST_GeodesicDistance calcularão a distância entre duas localizações de entrada. A unidade para ST_Distance é a mesma das geometrias de entrada. Para ST_GeodesicDistance a distância é calculada em metros. - ST_Length e ST_GeodesicLength calcularão o comprimento de uma feição de entrada. A unidade para ST_Length é a mesma da geometria de entrada. Para ST_GeodesicLength o comprimento é calculado em metros.
Vamos analisar esses cálculos usando as linhas que geramos anteriormente conectando locais de escolas às solicitações de serviço próximas.
Com o cálculo de distância, também podemos observar um recurso particularmente interessante da biblioteca GeoAnalytics - projeção dinâmica. Se você quiser calcular uma propriedade ou relação usando duas geometrias que estão em diferentes sistemas de coordenadas, a biblioteca pode frequentemente reconciliar essa diferença para você, para que não precise transformar os dados.
- A coluna geometry (solicitações de serviço) está no sistema de coordenadas Massachusetts State Plane (SRID 2249)
- A coluna geometry_school (localizações das escolas) está em latitude e longitude WGS84 (SRID 4326)
Vamos usar essas duas geometrias e calcular uma variedade de cálculos de distância e ver como o GeoAnalytics pode trabalhar com geometrias em diferentes sistemas de coordenadas! Na célula abaixo estamos calculando:
- Distância entre as duas geometrias sem transformar as coordenadas
- Distância entre as duas geometrias com uma transformação para converter a geometry_school para Massachusetts State Plane (2249)
- Distância geodésica entre as duas geometrias sem transformação
Também calcularemos o comprimento da linha geometry_line que criamos anteriormente para conectar os dois pontos. Faremos esses cálculos e exibiremos os resultados como exemplo:
display(
df_schools_requests.select("geometry",
"geometry_school",
F.round(ST.distance("geometry", "geometry_school"), 2).alias("Distância (sem transformação)"),
F.round(ST.distance("geometry", ST.transform("geometry_school", 2249)),2).alias("Distância (com transformação)"),
F.round(ST.geodesic_distance("geometry", "geometry_school"),2).alias("DistânciaGeodésica (metros)"),
"geometry_line",
F.round(ST.length("geometry_line"),2).alias("Comprimento (pés)"),
F.round(ST.geodesic_length("geometry_line"),2).alias("ComprimentoGeodésico (metros)")
)
)

Isto mostra como você pode realizar esses cálculos, mesmo com geometrias em diferentes sistemas de coordenadas! GeoAnalytics for Fabric é excelente para ajudar a mitigar diferenças em sistemas de coordenadas sempre que possível.
Agora vamos dar uma última olhada em algumas das propriedades que podemos calcular e explorar área.
ST_Area e ST_GeodesicArea
ST_Area - calcula uma área planar para cada geometria. A unidade da área é a mesma das geometrias de entrada. Se você calcular área em um conjunto de dados em um sistema de coordenadas geográfico, as unidades serão graus decimais quadrados. Esta não é uma unidade recomendada. Para usar a função ST_Area é melhor usar um sistema de coordenadas projetado. Mais informações estão disponíveis no documento conceitual Sistemas de Coordenadas e Transformações.
ST_GeodesicArea calcula a área para cada geometria em metros quadrados, e requer que uma referência espacial esteja definida na coluna da geometria de entrada.
Para este exemplo, vamos calcular áreas para estados dos EUA que lemos a partir de um serviço de feição ArcGIS.<\/SPAN>
<\/P>
<\/P>
Antes de calcular a área, devemos verificar o sistema de coordenadas dos dados para que saibamos mais sobre as unidades usadas no sistema e decidir se vamos usar <\/SPAN>ST_Area ou ST_GeodesicArea. Podemos fazer isso com a função get_spatial_reference()
df_states.st.get_spatial_reference()
Que retorna informações mostrando que os dados têm um SRID de 4326 (sistema de coordenadas WGS84)
SpatialReference(srid=4326, is_projected=False, unit='Degree', wkt='GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\",6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]]')
Para este conjunto de dados, vamos olhar os valores calculados por ST_Area e ST_GeodesicArea.
No exemplo abaixo exibimos os dados originais para o nome do estado e milhas quadradas, e três novos campos calculados, todos arredondados para duas casas decimais para legibilidade usando PySpark Round()
- área usando ST_Area - já que a referência espacial é 4326 com unidade Grau, os resultados estarão em graus decimais quadrados, que não tem valor aplicável real
- área usando ST_Area em uma geometria transformada usando projeção USA Contiguous Albers Equal Area (102003). Este cálculo retorna metros quadrados, então são convertidos para milhas quadradas no exemplo abaixo
- área usando ST_GeodesicArea na geometria original. Este cálculo retorna metros quadrados, então são convertidos para milhas quadradas na exemplo abaixo
Os valores retornados estão todos muito próximos uns dos outros em valor, mas não exatamente iguais. Essas variações se devem à distorção no cálculo da área para grandes regiões (o documento do conceito principal Sistemas de Coordenadas e Transformações fornece mais informações sobre distorção em projeções), bem como pequenas variações na precisão que podem ser introduzidas com o processo de projeção e transformação.
# Ao ler serviços de feição, a geometria geralmente está em uma coluna chamada "Shape." Isso pode ser confirmado olhando a tabela exibida acima
display(
df_states.select("STATE_NAME",
"SQMI",
F.round(ST.area("Shape"), 2).alias("Área (graus decimais quadrados"),
F.round(ST.area(ST.transform("Shape", 102003))/2.59e+6, 2).alias("Área (sqmi)"),
F.round(ST.geodesic_area("Shape")/2.59e+6, 2).alias("Área Geodésica (sqmi)")
)
)

Conclusão
Esperamos que este rápido guia sobre como criar novas geometrias e calcular suas propriedades tenha sido útil. Publicaremos conteúdo adicional para ajudá-lo a começar, então volte à Comunidade para mais! E por favor, nos informe sobre que tipo de coisas você gostaria de aprender mais!