Para ayudar a iniciar la Vista Previa Pública de ArcGIS GeoAnalytics para Microsoft Fabric, tendremos una serie de publicaciones en el blog explorando la funcionalidad de GeoAnalytics para Fabric. En esta publicación, exploraremos los fundamentos de calcular / construir nuevas geometrías como buffers, spatial bins y shortest paths. También veremos cómo calcular propiedades como la longitud y el área de estas nuevas geometrías.
Importando GeoAnalytics para Fabric
Comenzaremos con un repaso sobre cómo importar GeoAnalytics para Fabric y comenzar a trabajar con él. Para importar GeoAnalytics para Fabric solo necesitas agregar import geoanalytics_fabric a una celda en tu notebook. Para más información sobre cómo habilitar la biblioteca, consulta la documentación de Inicio Rápido.
Para mayor comodidad, también recomendamos importar las funciones geoespaciales directamente, y darles un alias fácil de usar para referencia. En el ejemplo a continuación, importamos la biblioteca y las funciones con un alias de ST. Dado que cada una de las funciones se nombran ST.<function_name>, esto facilita su referencia e identifica que son funciones Spatial Type (ST). Esto también se alineará con la estructura de ejemplos a lo largo de la documentación de ArcGIS GeoAnalytics. Tu celda se verá así:
# import ArcGIS GeoAnalytics
import geoanalytics_fabric
import geoanalytics_fabric.sql.functions as ST
Por ejemplo, después de importar, podrías referenciar la función ST_Buffer usando ST.buffer en lugar de geoanalytics_fabric.sql.functions.buffer.
Calculando nuevas geometrías
GeoAnalytics para Fabric contiene varias operaciones espaciales para generar nuevas geometrías basadas en la entrada. Por ejemplo, exploraremos algunas funciones comunes en esta publicación:
Aunque exploraremos algunas funciones en detalle, hay muchas más en la biblioteca GeoAnalytics para Fabric que pueden ser de interés para tus proyectos analíticos. Puedes explorar el rango de funciones en la documentación de Funciones SQL.
¡Comencemos con algunos buffers!
ST_Buffer & ST_GeodesicBuffer
ST_Buffer y ST_GeodesicBuffer crean polígonos que representan el área menor o igual a la distancia especificada alrededor de la geometría de entrada (por ejemplo, la región dentro de 10 millas alrededor de un punto). Estas funciones crearán este buffer alrededor de cada entidad en la columna geometry.
Para ver cómo funciona esto, comenzaremos creando un nuevo DataFrame con algunas ubicaciones escolares en el área de Boston. Bufferizaremos esas ubicaciones para observar visualmente el área alrededor de cada escuela. Aquí hay un ejemplo de creación de un nuevo DataFrame con algunas ubicaciones puntuales:
# crear un DataFrame con ubicaciones de varias escuelas (en latitud y longitud)
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"])
# crear una columna geometry tipo punto a partir de los valores latitud y longitud
df_schools = df_schools\
.withColumn("geometry", ST.point("longitude", "latitude", sr=4326))
Aquí están los resultados:

Ahora podemos crear buffers alrededor de estas escuelas.
La unidad de distancia para el buffer está en las mismas unidades que la geometría de entrada. Por ejemplo, si los datos están en el sistema coordenado web Mercator, las unidades serán metros; si tus datos están en un sistema coordenado geográfico, las unidades serán grados decimales.
La función ST_GeodesicBuffer puede usarse para crear buffers usando distancia geodésica en metros. Vamos a crear algunos buffers y observarlos - y ver cómo difieren el buffer planar y el buffer geodésico.
# crear buffers ejemplo usando ST_Buffer y ST_GeodesicBuffer
# Este ejemplo genera un buffer de 0.5 _grados decimales_ (las unidades para los datos son valores latitud / longitud!)
# También crea un 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))
Primero, veamos el buffer planar - que fue calculado en las unidades de la geometría entrada (grados decimales). Este es un buffer de 0.5 grados decimales alrededor de las tres escuelas:

Nótese que dejé las etiquetas del eje en el mapa para que puedas ver que esto se está graficando sobre un mapa base en grados decimales.
Y ahora, veamos los buffers geodésicos alrededor de las escuelas. Nótese que esto también se está graficando sobre un mapa base en grados decimales. Podemos ver que los buffers son mucho más pequeños (500m alrededor de cada escuela), y que no son circulares. Exploremos por qué es esto...

Hay algunas cosas clave a notar al graficar estos buffers:
- Los buffers en el primer gráfico parecen enormes, mientras que los buffers en el segundo son mucho más pequeños. El "geometry_buffer" es medio grado decimal, mientras que el "geometry_geodesic_buffer" es solo 500 metros. Un grado decimal es significativamente más largo que 500 metros. En el ecuador, un grado de latitud o longitud es cercano a 111 millas!
- Los buffers en el primer gráfico ("geometry_buffer") parecen circulares mientras que los buffers en el segundo gráfico ("geometry_geodesic_buffer") parecen óvalos. ¿Por qué es esto? Ambos mapas están generados en un mapa basado en las coordenadas de latitud y longitud. En estas gráficas, un grado de latitud es igual a un grado de longitud en longitud. En la tierra, la longitud de un grado de latitud y longitud difiere, siendo un grado de longitud cada vez más corto a medida que te acercas a los polos. La entrada de Wikipedia sobre <\/SPAN>Longitude<\/A> <\/SPAN>discute esto con buen detalle si deseas más información. Cuando los buffers geodésicos se trazan en esta proyección cartográfica en particular, parecen estirados en óvalos, aunque, en la superficie terrestre serían circulares.<\/LI><\/UL>Si trazáramos el buffer geodésico en nuestro sistema local de coordenadas de Boston (SRID 2249), los buffers se ven como los círculos que esperaríamos.<\/P>Podemos actualizar el mapa cuando lo trazamos añadiendo un parámetro <\/SPAN>sr<\/FONT> <\/SPAN>(referencia espacial). El código de ejemplo a continuación demuestra esto. Más información sobre los parámetros para <\/SPAN>st.plot<\/FONT> <\/SPAN>se puede encontrar en la <\/SPAN>referencia API<\/A>.<\/P>
<\/P>
# trazando geometry_geodesic_buffer en un mapa con referencia espacial 2249
df_schools\\
.st.plot(basemap="streets", geometry="geometry_geodesic_buffer", alpha=0.5, edgecolor="black", sr=2249);<\/code><\/pre><P> <\/P><P>En general, es más rápido ejecutar<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>función para identificar el<SPAN> </SPAN><A href="https://h3geo.org/" target="_blank" rel="noopener nofollow noreferrer">bin H3</A><SPAN> </SPAN>en el que se encuentra cada uno de nuestros puntos de solicitud de servicio. Luego usaremos una función 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 crear un DataFrame simplificado que liste el conteo de puntos dentro de cada bin.</P><P>El tamaño de los bins H3 se basa en un valor de resolución. Para este ejemplo, usaremos una resolución de 8, que equivale a un tamaño promedio de bin hexagonal de 0.73 kilómetros cuadrados. Más información sobre<SPAN> </SPAN><A href="https://h3geo.org/docs/core-library/restable" target="_blank" rel="noopener nofollow noreferrer">resoluciones de bins H3 se puede encontrar aquí</A>.</P><P>Agregar el ID para un bin es tan fácil como crear una nueva columna para los datos y usar la función bin para el tipo de geometría que desees (hexagonal, cuadrado o H3):</P><P> </P><pre class="lia-code-sample language-python"><code># agregar una columna con el _id_ del bin H3 en el que cae cada punto
df = df\
.withColumn("h3_bin", ST.h3_bin("geometry", 8))
La
función ST_H3Bin agrega un id de bin a cada registro. No agrega la geometría específicamente, ya que habría muchas duplicaciones de la geometría (una por cada punto dentro del bin) y es más eficiente trabajar con el ID hasta que necesitemos las geometrías para análisis o visualización. Podemos ver estos nuevos IDs de bin en la tabla a continuación:
Usando el campo ID h3_bin, podemos agrupar los valores en nuestra tabla para contar los registros en cada bin. # agrupar el DataFrame de solicitudes de servicio usando el ID del bin h3 y contar todos los registros
df_by_bin = df.groupBy("h3_bin").count() Después de agrupar, podemos ver que cada ID de bin tiene un conteo del número de puntos dentro. Después de hacer esto, podemos convertir los IDs de bin en geometrías y visualizar los resultados.
Aquí está cómo agregamos la geometría del bin a la tabla usando la función ST_BinGeometry: # agregar la geometría del bin basada en el ID del bin usando ST_BinGeometry
df_by_bin = df_by_bin\
.withColumn("geometry_bin", ST.bin_geometry("h3_bin")) Graficar los resultados ahora nos muestra dónde ocurre la mayor concentración de solicitudes de servicio:
Ahora pasemos a calcular algunas líneas y distancias entre ubicaciones!ST_ShortestLine y ST_GeodesicLineST_ShortestLine devuelve una columna linestring que representa la línea más corta que toca dos geometrías, usando cálculo planar. Esta función devuelve solo una línea más corta si hay más de una que se pueda calcular entre ubicaciones.Para crear una línea más corta usando cálculos de distancia geodésica, usa ST_GeodesicShortestLine.Como ejemplo, veremos la línea más corta entre el DataFrame schools que creamos antes y las solicitudes de servicio cercanas. La línea más corta usará dos columnas geométricas como entrada, por lo que deben estar en el mismo DataFrame.En los ejemplos anteriores con buffers, usamos ST_Contains para encontrar todos los puntos dentro del polígono buffer.En este ejemplo, usaremos otro cálculo espacial llamado ST_DWithin (distancia dentro) para encontrar todos los puntos dentro de una distancia especificada desde los puntos escolares. Efectivamente esto puede dar el mismo resultado, sin embargo DWithin suele ser más eficiente que crear un buffer y realizar un join espacial. DWithin usa cálculos planos por defecto, así que deberíamos transformar la geometría a un sistema local o establecer el parámetro para usar cálculos geodésicos.En este caso, usaremos ST_Transform para actualizar el sistema coordenado para este cálculo. Lo haremos en un solo cálculo donde unimos las escuelas y solicitudes y creamos un nuevo DataFrame con los resultados: # encontrar todos los puntos dentro de 500 pies de cada escuela
# usar un join espacial para emparejar cualquier punto dentro de la distancia especificada a la escuela
# dado que los datasets están originalmente en coordenadas latitud/longitud, transformamos primero a un 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)) Ahora que cada escuela tiene la geometría para la escuela y los puntos cercanos, podemos usar esas dos geometrías para calcular la línea más corta entre cada punto de solicitud y la escuela. # calcular la línea más corta desde cada punto a cada escuela
df_schools_requests = df_schools_requests\
.withColumn("geometry_line", ST.shortest_line("geometry", "geometry_school")) Echemos un vistazo al resultado para una escuela: # graficar las líneas conectores entre una escuela y todos los puntos dentro de 500 pies de esta ubicación
df_schools_requests.filter(F.col("name") == "Hurley K-8").st.plot(basemap="streets", geometry="geometry_line", linewidth=0.5, alpha=0.7);
Para distancias cortas, ST_ShortestLine es una buena opción, pero para distancias largas podrías considerar usar ST_GeodesicShortestLine para que la línea creada represente el camino más corto sobre la esfera. Como ejemplo de la diferencia entre la "línea más corta" (cálculo planar) y la línea geodésica, podemos explorar la diferencia entre una línea conectada entre Los Ángeles y Londres. En este ejemplo, la línea geodésica (naranja) aparece curva y más larga en esta proyección, ¡pero es realmente la línea más corta! # línea geodésica (geodesic) (en naranja) vs. "línea más corta" (planar) (en azul)
# la línea geodésica aparece curva y más larga en esta proyección, ¡pero es realmente la línea más corta!
# crear puntos para dos ubicaciones
data = [
("POINT (-118.2426 34.0549 )", "Los Angeles",
"POINT (0.1276 51.5072 )", "London")
]
# crear un DataFrame con las dos ubicaciones
# convertir las ubicaciones de well-known text a geometría
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"))
# graficar los resultados para comparar las dos líneas generadas (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 Distancia y LongitudAhora veamos la distancia y longitud para estas líneas...ST_Distance y ST_GeodesicDistance calcularán la distancia entre dos ubicaciones de entrada. La unidad para ST_Distance es la misma que las geometrías de entrada. Para ST_GeodesicDistance la distancia se calcula en metros. - ST_Length y ST_GeodesicLength calcularán la longitud de una característica de entrada. La unidad para ST_Length es la misma que la geometría de entrada. Para ST_GeodesicLength la longitud se calcula en metros.
Examinaremos estos cálculos usando las líneas que generamos anteriormente conectando ubicaciones escolares con las solicitudes de servicio cercanas.
Con el cálculo de distancia, también podemos observar una característica particularmente útil de la biblioteca GeoAnalytics: proyección en tiempo real. Si desea calcular una propiedad o relación usando dos geometrías que están en diferentes sistemas de coordenadas, la biblioteca a menudo puede reconciliar esta diferencia por usted para que no necesite transformar los datos.
- La columna geometry (solicitudes de servicio) está en el sistema de coordenadas Massachusetts State Plane (SRID 2249)
- La columna geometry_school (ubicaciones escolares) está en latitud y longitud WGS84 (SRID 4326)
Usemos estas dos geometrías y calculemos una variedad de cálculos de distancia para ver cómo GeoAnalytics puede trabajar con geometrías en diferentes sistemas de coordenadas! En la celda a continuación estamos calculando:
- Distancia entre las dos geometrías sin transformar las coordenadas
- Distancia entre las dos geometrías con una transformación para convertir la geometry_school a Massachusetts State Plane (2249)
- Distancia geodésica entre las dos geometrías sin transformación
También calcularemos la longitud de la línea geometry_line que creamos anteriormente para conectar las dos ubicaciones puntuales. Realizaremos estos cálculos y mostraremos los resultados como ejemplo:
display(
df_schools_requests.select("geometry",
"geometry_school",
F.round(ST.distance("geometry", "geometry_school"), 2).alias("Distancia (sin transformar)"),
F.round(ST.distance("geometry", ST.transform("geometry_school", 2249)),2).alias("Distancia (transformada)"),
F.round(ST.geodesic_distance("geometry", "geometry_school"),2).alias("DistanciaGeodésica (metros)"),
"geometry_line",
F.round(ST.length("geometry_line"),2).alias("Longitud (pies)"),
F.round(ST.geodesic_length("geometry_line"),2).alias("LongitudGeodésica (metros)")
)
)

Esto muestra cómo puede realizar estos cálculos, incluso con geometrías en diferentes sistemas de coordenadas! GeoAnalytics for Fabric es excelente para ayudar a mitigar diferencias en sistemas de coordenadas donde sea posible.
Ahora echemos un último vistazo a algunas de las propiedades que podemos calcular y exploremos el área.
ST_Area y ST_GeodesicArea
ST_Area - calcula un área planar para cada geometría. La unidad del área es la misma que las geometrías de entrada. Si calcula el área en un conjunto de datos en un sistema de coordenadas geográfico, las unidades serán grados decimales cuadrados. Esta no es una unidad recomendada. Para usar la función ST_Area es mejor usar un sistema de coordenadas proyectado. Más información está disponible en el documento conceptual principal Sistemas de Coordenadas y Transformaciones.
ST_GeodesicArea calcula el área para cada geometría en metros cuadrados, y requiere que se establezca una referencia espacial en la columna de geometría de entrada.
Para este ejemplo, calcularemos áreas para los estados de EE.UU. que leímos desde un servicio de entidades ArcGIS.<\/SPAN>
<\/P>
<\/P>
Antes de calcular el area, deberíamos verificar el sistema de coordenadas del conjunto de datos para saber más sobre las unidades usadas en el sistema y decidir si vamos a usar <\/span>
ST_Area<\/A>o <\/span>
ST_GeodesicArea<\/A>. Podemos hacer esto con la función get_spatial_reference()
<\/P>
<\/P>
Lo cual devuelve información mostrando que los datos tienen un SRID 4326 (sistema de coordenadas WGS84)<\/p>
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]]')<\/span>
<\/PRE>
Para este conjunto, veamos los valores calculados por <\/span>
ST_Area<\/A>y <\/span>
ST_GeodesicArea<\/A>.
En el ejemplo a continuación mostramos los datos originales para el nombre del estado y millas cuadradas, y tres nuevos campos calculados, todos redondeados a dos decimales para facilitar la lectura usando PySpark <\/span>
Round()<\/A>
- rea usando ST_Area - dado que la referencia espacial es 4326 con unidad Degree, los resultados estarán en grados decimales cuadrados, lo cual no tiene un valor aplicable real
- rea usando ST_Area sobre una geometría transformada usando proyección USA Contiguous Albers Equal Area (102003). Este cálculo devuelve metros cuadrados, por lo que se convierten a millas cuadradas en el ejemplo a continuación
- rea usando ST_GeodesicArea sobre la geometría original. Este cálculo devuelve metros cuadrados, por lo que se convierten a millas cuadradas en el ejemplo a continuación
Los valores devueltos están todos muy cerca unos de otros en valor, pero no exactamente iguales. Estas desviaciones se deben a la distorsión en el cálculo del área para regiones grandes (el
documento del concepto central Sistemas de Coordenadas y Transformaciones proporciona más información sobre la distorsión en las proyecciones), así como a variaciones menores en la precisión que pueden introducirse con el proceso de proyección y transformación. # Al leer servicios de entidades, la geometría generalmente está en una columna llamada "Shape." Esto se puede confirmar mirando la tabla mostrada arriba
display(
df_states.select("STATE_NAME",
"SQMI",
F.round(ST.area("Shape"), 2).alias("Área (grados decimales cuadrados"),
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)")
)
)

Conclusión
Esperamos que esta rápida introducción sobre cómo crear nuevas geometrías y calcular sus propiedades haya sido útil. Publicaremos contenido adicional para ayudarte a comenzar, así que vuelve a consultar la Comunidad para más información! Y por favor, ¡háznos saber qué tipo de cosas te gustaría aprender más!