How can I make a map using GeoJSON data in Altair?
In this example, data.us_10m.url
is a string variable, where the string specifies the URL to a geojson file containing US state boundaries in the state
feature. If you have a different geojson file you would like to use, you can substitute its URL in that example.
The example you refer to is using topojson
structured data, while you have geojson
structured data. So you probably need:
# remote geojson data object
url_geojson = 'https://raw.githubusercontent.com/mattijn/datasets/master/two_polygons.geo.json'
data_geojson_remote = alt.Data(url=url_geojson, format=alt.DataFormat(property='features',type='json'))
# chart object
alt.Chart(data_geojson_remote).mark_geoshape(
).encode(
color="properties.name:N"
).project(
type='identity', reflectY=True
)
Update: GeoDataFrames (geopandas) are directly supported since Altair version 3.3.0. So do any objects that support the __geo_interface__
.
For more insight read on!
Here below is discussed the variants:
- Inline GeoJSON
- Inline TopoJSON
- TopoJSON from URL
- GeoJSON from URL
Explaining the differences between geojson
and topojson
structured json
files and their usage within Altair
import geojson
import topojson
import pprint
import altair as alt
Inline GeoJSON
We start with creating a collection containing two features, namely two adjacent polygons.
Example of the two polygons that we will create in the GeoJSON data format.:
feature_1 = geojson.Feature(
geometry=geojson.Polygon([[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]]),
properties={"name":"abc"}
)
feature_2 = geojson.Feature(
geometry=geojson.Polygon([[[1, 0], [2, 0], [2, 1], [1, 1], [1, 0]]]),
properties={"name":"def"}
)
var_geojson = geojson.FeatureCollection([feature_1, feature_2])
Inspect the created GeoJSON by pretty print the variable var_geojson
pprint.pprint(var_geojson)
{'features': [{'geometry': {'coordinates': [[[0, 0],
[1, 0],
[1, 1],
[0, 1],
[0, 0]]],
'type': 'Polygon'},
'properties': {'name': 'abc'},
'type': 'Feature'},
{'geometry': {'coordinates': [[[1, 0],
[2, 0],
[2, 1],
[1, 1],
[1, 0]]],
'type': 'Polygon'},
'properties': {'name': 'def'},
'type': 'Feature'}],
'type': 'FeatureCollection'}
As can be seen, the two Polygon
Features
are nested within the features
object and the geometry
is part of each feature
.
Altair has the capability to parse nested json
objects using the property
key within format
. The following is an example of such:
# inline geojson data object
data_geojson = alt.InlineData(values=var_geojson, format=alt.DataFormat(property='features',type='json'))
# chart object
alt.Chart(data_geojson).mark_geoshape(
).encode(
color="properties.name:N"
).project(
type='identity', reflectY=True
)
Inline TopoJSON
TopoJSON is an extension of GeoJSON, where the geometry
of the features
are referred to from a top-level object named arcs
. This makes it possible to apply a hash function on the geometry, so each shared arc
should only be stored once.
We can convert the var_geojson
variable into a topojson
file format structure:
var_topojson = topojson.Topology(var_geojson, prequantize=False).to_json()
var_topojson
{'arcs': [[[1.0, 1.0], [0.0, 1.0], [0.0, 0.0], [1.0, 0.0]],
[[1.0, 0.0], [2.0, 0.0], [2.0, 1.0], [1.0, 1.0]],
[[1.0, 1.0], [1.0, 0.0]]],
'objects': {'data': {'geometries': [{'arcs': [[-3, 0]],
'properties': {'name': 'abc'},
'type': 'Polygon'},
{'arcs': [[1, 2]],
'properties': {'name': 'def'},
'type': 'Polygon'}],
'type': 'GeometryCollection'}},
'type': 'Topology'}
Now the nested geometry
objects are replaced by arcs
and refer by index to the top-level arcs
object. Instead of having a single FeatureCollection
we now can have multiple objects
, where our converted FeatureCollection
is stored within the key data
as a GeometryCollection
.
NOTE: the key-name data
is arbitrary and differs in each dataset.
Altair has the capability to parse the nested data
object in the topojson
formatted structure using the feature
key within format
, while declaring it is a topojson
type
. The following is an example of such:
# inline topojson data object
data_topojson = alt.InlineData(values=var_topojson, format=alt.DataFormat(feature='data',type='topojson'))
# chart object
alt.Chart(data_topojson).mark_geoshape(
).encode(
color="properties.name:N"
).project(
type='identity', reflectY=True
)
TopoJSON from URL
There also exist a shorthand to extract the objects from a topojson
file if this file is accessible by URL:
alt.topo_feature(url, feature)
Altair example where a topojson
file is referred by URL
# remote topojson data object
url_topojson = 'https://raw.githubusercontent.com/mattijn/datasets/master/two_polygons.topo.json'
data_topojson_remote = alt.topo_feature(url=url_topojson, feature='data')
# chart object
alt.Chart(data_topojson_remote).mark_geoshape(
).encode(
color="properties.name:N"
).project(
type='identity', reflectY=True
)
GeoJSON from URL
But for geojson
files accessible by URL there is no such shorthand and should be linked as follows:
alt.Data(url, format)
Altair example where a geojson
file is referred by URL
# remote geojson data object
url_geojson = 'https://raw.githubusercontent.com/mattijn/datasets/master/two_polygons.geo.json'
data_geojson_remote = alt.Data(url=url_geojson, format=alt.DataFormat(property='features',type='json'))
# chart object
alt.Chart(data_geojson_remote).mark_geoshape(
).encode(
color="properties.name:N"
).project(
type='identity', reflectY=True
)