Leyendo la información geográfica en GeoJSON

1 Transmitiendo datos geográficos

En el mundo de D3, los datos geográficos se comparten principalmente en 2 formatos: TopoJSON y GeoJSON; ambos siguen el estándar JSON.

GeoJSON es un estándar para representar información geográfica. Los bloques de información que puede contener incluyen: puntos (con ello direcciones y locaciones), segmentos de líneas (con ello calles, autopistas y límites), polígonos (países, provincias, segmentos de tierra), así como colecciones con diversas combinaciones de estos bloques. Es interesante notar que estos bloques de información no necesariamente requieren representar entidades físicas; por ejemplo, por lo regular los estados dentro de un país no están físicamente separados uno del otro; no hay una barrera física que te impida moverte de un estado al otro; no obstante, no hay ningún problema en crear un objeto de GeoJSON que represente a un estado.

Por otro lado, TopoJSON es un sub-set de GeoJSON; es decir, el primero está basado en el segundo. A diferencia de GeoJSON, D3 no puede trabajar directamente con TopoJSON; por ello, es necesario traducirlo al estándar GeoJSON antes de ser usado. Entonces, te podrías estar preguntando: ¿de qué sirve TopoJSON si de cualquier manera no se puede emplear directamente en D3? pues bien, por la manera en la que está estructurado, los archivos en TopoJSON son mucho más pequeños y pueden contener más información que los archivos con datos en GeoJSON. Esto puede ser una enorme ventaja, por ejemplo, si tienes un servidor que provee de datos geográficos; ya que tu objetivo es mantener al servidor ágil, te conviene que éste transfiera archivos pequeños a los clientes y que precisamente sean los clientes quienes se encarguen de procesar y transformar estos datos. Adicionalmente, el estándar TopoJSON está diseñado para considerar la topología del suelo, es decir, la altitud del terreno; ésto no se considera de manera nativa en el estándar GeoJSON.

Por cierto, si quieres conocer al standard GeoJSON completo, puedes consultarlo en geojson.org

2 Estructura de GeoJSON

2.1 ¿Qué se necesita para ser un objeto GeoJSON?

2.1.1 Los tipos de objeto GeoJSON

Existen 3 grandes “especies” de objetos GeoJSON:

  • Geometry: Es una región de espacio en su sentido más amplio. Cualquier región espacial que pueda ser ubicada en un espacio coordenado (por ejemplo, coordenadas de longitud y latitud), puede ser descrita dentro de un objeto Geometry
  • Feature: Se trata de una entidad ligada espacialmente. Esto quiere decir que además de contener información espacial, también contiene información que describe qué es esa región espacial. Para aclarar este punto, considera esto: para representar a un país necesitamos no solo la ubicación del país; otros datos relevantes para identificarlo pueden ser, por ejemplo, su nombre completo, un nombre abreviado, el continente en que se encuentra, qué idioma se habla en él, etc. Otros ejemplos de entidades ligadas espacialmente pueden ser: estados, municipios, alcaldías, colonias, etc.
  • FeatureCollection: Es una colección de objetos Feature.

A su vez, existen 7 tipos de objetos Geometry, por lo que puede haber 9 tipos de objetos GeoJSON.

2.1.2 La propiedad type

Todo objeto GeoJSON debe tener una propiedad llamada type; en esta, debe indicarse alguno de los 9 tipos de objetos GeoJSON: “Point”, “LineString”, “Polygon”, “MultiPoint”, “MultiLineString”, “MultiPolygon”, “GeometryCollection”, “Feature” o “FeatureCollection”.

2.1.3 Propiedades arbitrarias en los objetos GeoJSON

También es válido que los objetos GeoJSON tengan cualquier otra propiedad arbitraria no indicada en la especificación de GeoJSON. Los objetos ligados a dichas propiedades son llamados foreign-objects y jamás se les considera objetos GeoJSON, sin importar el nombre de sus propiedades. Tal vez te estés preguntando: ¿Cuál es el caso de añadir propiedades arbitrarias a los objetos de GeoJSON? después de todo, lo que queremos es transmitir información cartográfica, ¿no es cierto? Pues bien, este tipo de propiedades arbitrarias pueden ser sumamente útiles para identificar información sobre las regiones espaciales indicadas en el objeto GeoJSON; por ejemplo, podemos indicar una propiedad para el nombre del país, otra para el idioma que se habla en el mismo, una más para el número de habitantes, etc. Recuerda, las visualizaciones geo-referenciadas únicamente son útiles si te ayudan a contar una historia sobre tus datos; estas propiedades arbitrarias son un punto en donde se pueden almacenar los datos sobre los cuales quieres contar una historia.

2.2 El Objeto Geometry

2.2.1 Fundamentos

Recuerda, un objeto Geometry representan puntos, curvas y superficies en un espacio coordenado; estas coordenadas pueden ser en longitud y latitud.

2.2.1.1 Propiedad coordinates

Con excepción del objeto GeometryCollection, todos los objetos Geometry deben tener una propiedad llamada coordinates, en la cual se indica la información sobre su posición.

2.2.1.2 Posición y posición-base

La posición es la construcción fundamental de un objeto Geometry.

En su forma más básica, una posición es un arreglo de números decimales; a esto se le llama posición-base. Los primeros dos elementos del arreglo son la longitud y latitud, en ese orden. Opcionalmente, puede haber un tercer número que represente la altitud; sin embargo, no todos los parsers para GeoJSON son capaces de interpretar este dato.

Es interesante notar que la información sobre la posición se construye de diferentes maneras en diferentes objetos Geometry; por ejemplo, se puede usar una sola posición-base en el caso del objeto Point; y un arreglo de posición-base en el caso de LineString o MultiPoint; como veremos a continuación.

2.2.2 Tipos de objeto Geometry

Hay siete tipos de objeto Geometry; por lo tanto, la propiedad type debe ser alguno de los siguientes valores: “Point”, “LineString”, “Polygon”, “MultiPoint”, “MultiLineString”, “MultiPolygon” o “GeometryCollection”.

2.2.2.1 Point

  • Es un solo punto en un mapa.
  • La propiedad coordinates es una sola posición-base; por ejemplo: [-109.952621, 22.873550].

2.2.2.2 MultiPoint

  • Es un conjunto de puntos en el mapa.
  • La propiedad coordinates es un arreglo de posiciónes-base; por ejemplo: [[-109.952621, 22.873550], [-117.152752, 32.511857], [-106.389995, 31.729324]]

2.2.2.3 LineString

  • Es una línea en un mapa, donde se necesitan al menos 2 puntos para formar la línea.
  • La propiedad coordinates es un arreglo de dos o más posición-base, tal como en el caso del objeto MultiPoint.
  • La diferencia con el objeto MultiPoint es que, en éste último, el conjunto de puntos no forman una línea; sino que permanecen como un grupo de puntos independientes.

2.2.2.4 MultiLineString

En este caso, la propiedad coordinates es un arreglo de posiciones como las indicadas en el objeto LineString, esto es, un arreglo de arreglos de arreglos (tres niveles); por ejemplo:

[
    [[-101.830050, 29.821814], [-97.103269, 25.889812], [-97.366612, 21.168504]]
    ,[[-86.818797, 21.067803], [-92.307206, 14.541265], [-94.745877, 16.296351]]
    ,[[-105.705476, 20.346444], [-114.719173, 31.663829], [-109.396981, 23.349035]]
]

2.2.2.5 Polygon

Para comprender mejor al objeto Polygon, es necesario introducir un nuevo concepto: linear-ring:

  • Un linear-ring no es en sí un objeto de GeoJSON; pero es un concepto necesario para entender a los objetos Polygon.
  • Un linear-ring puede pensarse como un objeto LineString cerrado.
  • El linear-ring es un arreglo con al menos 4 posición-base; donde la primera y última de las posiciones-base son equivalentes y siempre deben tener valores idénticos.
  • Un ejemplo de un linear-ring sería el siguiente arreglo: [[-105.705476, 20.346444], [-114.719173, 31.663829], [-109.396981, 23.349035], [-105.705476, 20.346444]]
  • Un linear-ring puede representar los límites de una superficie, en cuyo caso sería un anillo exterior; sólo puede haber un anillo exterior en el objeto Polygon. El linear-ring también puede representar los límites de un hoyo en la superficie; es este caso sería un anillo interior; es válido tener más de un anillo interior

Una vez aclarado el punto del linear-ring, se vuelve mucho más sencillo definir al objeto Polygon:

  • La propiedad coordinates del objeto Polygon, debe ser un arreglo de linear-ring.
  • Para objetos Polygon con más de un linear-ring, el primero debe representar a un anillo exterior y todos los demás a anillos interiores.
  • Un ejemplo de la propiedad coordinates sería el siguiente arreglo:
[
    [[1, 7], [2, 2], [8, 2], [9, 8], [4, 9], [1, 7]]
    ,[[2, 7], [3, 8], [3, 6], [2, 7]]
    ,[[3, 3], [5, 4], [6, 3], [3, 3]]
    ,[[6, 7], [8, 7], [6, 5], [6, 7]]
]

2.2.2.6 MultiPolygon

Para este objeto, la propiedad coordinates es un arreglo de coordenadas como las indicadas en el objeto Polygon; por ejemplo:

[
	[
		[[0, 6], [1, 1], [7, 1], [8, 7], [3, 8], [0, 6]]
		,[[1, 6], [2, 7], [2, 5], [1, 6]]
		,[[2, 2], [4, 3], [5, 2], [2, 2]]
		,[[5, 6], [7, 6], [5, 4], [5, 6]]
	]
	,[
		[[1, 7], [2, 2], [8, 2], [9, 8], [4, 9], [1, 7]]
		,[[2, 7], [3, 8], [3, 6], [2, 7]]
		,[[3, 3], [5, 4], [6, 3], [3, 3]]
		,[[6, 7], [8, 7], [6, 5], [6, 7]]
	]
	,[
		[[2, 8], [3, 3], [9, 3], [10, 9], [5, 10], [2, 8]]
		,[[3, 8], [4, 9], [4, 7], [3, 8]]
		,[[4, 4], [6, 5], [7, 4], [4, 4]]
		,[[7, 8], [9, 8], [7, 6], [7, 8]]
	]
]

2.2.2.7 GeometryCollection

  • Este objeto tiene una propiedad llamada geometries, la cual es un arreglo de objetos Geometry. También es válido que este arreglo esté vacío.
  • Es el único objeto Geometry que puede estar compuesto de otros objetos Geometry.
  • Este objeto no tiene una propiedad coordinates.
  • Si bien es posible, no es deseable que un objeto GeometryCollection contenga a otro GeometryCollection.
  • Si lo piensas un momento, usar este objeto sólo tiene sentido si los sub-objetos que lo componen tienen diferentes tipos; de no ser así, en realidad sería mucho más conveniente usar MultiPoint, MultiLineString o MultiPolygon, según sea el caso.

2.3 El objeto Feature

Es un objeto GeoJSON que representa cualquier entidad delimitada espacialmente. La propiedad type de estos objetos debe tener el valor “Feature”.

Estos objetos tienen 3 propiedades inherentes a ellos:

  • geometry: El valor de esta propiedad sólo puede ser un objeto Geometry o null.
  • properties: En ella se puede indicar cualquier información que se desee; incluso, puede ligarse a un objeto JSON o al valor null.
  • id: Su valor puede ser un número o una cadena de texto. Sólo es utilizada cuando la información descrita por el objeto Feature tiene una identificación que es comúnmente utilizada; por ejemplo, el código numérico emitido por ISO 3166 para Argentina es 032. La propiedad id sería un buen sitio para ingresar esta información.

Como podrás notar, el objeto Feature es bastante útil; por un lado, puede almacenar información cartográfica en su propiedad geometry; y por otro lado, su propiedad properties es un excelente lugar para indicar cualquier otra colección de datos sobre la región espacial que el objeto Feature representa.

2.4 El objeto FeatureCollection

  • La propiedad type de estos objetos debe tener el valor “FeatureCollection”.
  • La otra propiedad inherente a este objeto es features, cuyo valor es un arreglo de objetos Feature.

3 Otra vez, por favor ¿qué objeto tiene qué propiedad?

¿Sientes tu cerebro confundido con tantas propiedades? ¡No temas! a continuación te presento una tabla que indica precisamente qué propiedades puede tener cada objeto. Recuerda, todos los objetos GeoJSON pueden además tener propiedades arbitrarias .

Tipo de objeto Propiedades
type coordinates geometries geometry properties id features
Geometry Point X X
LineString X X
Polygon X X
MultiPoint X X
MultiLineString X X
MultiPolygon X X
GeometryCollection X X
Feature X X X X
FeatureCollection X X

4 Ejemplo de un objeto GeoJSON

Y a todo esto ¿cómo luce un objeto GeoJSON? El siguiente ejemplo, tomado de tools.ietf.org te dará una idea clara. En este caso, toda la información está envuelta en un objeto FeatureCollection. En este punto, ya te debes ser capaz de leer éste objeto. Anda, ¡inténtalo!

    {
        "type": "FeatureCollection",
        "features": [
            {
                "type": "Feature",
                "geometry": {
                    "type": "Point",
                    "coordinates": [102.0, 0.5]
                },
                "properties": {
                    "prop0": "value0"
                }
            }, 
            {
                "type": "Feature",
                "geometry": {
                    "type": "LineString",
                    "coordinates": [
                        [102.0, 0.0],
                        [103.0, 1.0],
                        [104.0, 0.0],
                        [105.0, 1.0]
                    ]
                },
                "properties": {
                    "prop0": "value0",
                    "prop1": 0.0
                }
            }, 
            {
                "type": "Feature",
                "geometry": {
                    "type": "Polygon",
                    "coordinates": [
                        [
                            [100.0, 0.0],
                            [101.0, 0.0],
                            [101.0, 1.0],
                            [100.0, 1.0],
                            [100.0, 0.0]
                        ]
                    ]
                },
                "properties": {
                    "prop0": "value0",
                    "prop1": {
                        "this": "that"
                    }
                }
            }
        ]
    }

5 Conclusiones

Tal vez habrás notado que, de hecho, hay muchas posibilidades para estructurar la información geográfica. Es posible tener dos objetos GeoJSON válidos, estructurados de manera diferente pero que transmiten la misma información. Lo importante es que puedas identificar dónde está la información que te interesa utilizar para tu aplicación particular.

Finalmente, a menos que estés creando nueva información cartográfica, es poco probable que construyas tus propios objetos GeoJSON a mano. Usualmente, emplearás archivos que ya existen o usarás herramientas como mapshaper.org para transformar desde otro formato al estándar GeoJSON. Entonces ¿para qué molestarse en conocer este estándar?; el entender la manera en la que estos objetos funcionan, te ayudará a identificar y encontrar toda la información geográfica que los objetos GeoJSON usualmente contienen; de este modo, ¡puedes sacarles el máximo provecho para tus proyectos!