# swemaps
Maps of Sweden in [GeoParquet](https://github.com/opengeospatial/geoparquet) for easy usage.
The parquets have been created from files published by:
- [Statistics Sweden](https://www.scb.se/hitta-statistik/regional-statistik-och-kartor/regionala-indelningar/)
- [The Swedish Agency for Economic and Regional Growth](https://tillvaxtverket.se/tillvaxtverket/statistikochanalys/statistikomregionalutveckling/regionalaindelningar/faregioner.1799.html)
- [Swedish Election Authority](https://www.val.se/valresultat/riksdag-region-och-kommun/2022/radata-och-statistik.html)
The map data includes counties, municipalities, electoral districts and FA regions. The original geometries have been transformed from SWEREF 99 TM (EPSG:3006) to WGS 84 (EPSG:4326) for better out-of-the-box compatibility with interactive and web-based toolkits such as Folium and Plotly. The column names have also been somewhat sanitized (e.g. `KnKod` -> `kommun_kod`).
The package gets you the file path so that you can load it with your prefered tool, for example PyArrow or GeoPandas. An extra convenience function is included to quickly convert a PyArrow Table object to GeoJSON.
Made for Python with inspiration from [swemaps2](https://github.com/filipwastberg/swemaps2).
## Municipalities and counties
Municipalities | Counties
:-------------------------:|:-------------------------:
![municipalities](assets/ex1.png) | ![counties](assets/ex2.png)
### PyArrow example with Plotly
```python
>>> import plotly.express as px
>>> import pyarrow.parquet as pq
>>> import swemaps
# Load the map for the specified type
>>> kommuner = pq.read_table(swemaps.get_path("kommun"))
>>> kommuner.column_names
['kommun_kod', 'kommun', 'geometry']
# The convenience function returns GeoJSON from a PyArrow table object
>>> geojson = swemaps.table_to_geojson(kommuner)
# Here's a dataframe with municipalities and some random values that we can plot
>>> df.head()
shape: (5, 2)
┌──────────┬───────┐
│ Kommun ┆ Value │
│ --- ┆ --- │
│ str ┆ i64 │
╞══════════╪═══════╡
│ Ale ┆ 544 │
│ Alingsås ┆ 749 │
│ Alvesta ┆ 771 │
│ Aneby ┆ 241 │
│ Arboga ┆ 763 │
└──────────┴───────┘
# Use Plotly to create a choropleth using the dataframe and GeoJSON
>>> fig = px.choropleth(
df,
geojson=geojson,
color="Value",
locations="Kommun",
featureidkey="properties.kommun",
projection="mercator",
color_continuous_scale="Viridis",
fitbounds="locations",
basemap_visible=False,
)
```
You might want to subset the map of municipalities for a specific county or a group of counties. Since the geometry is loaded as a PyArrow table the filter operation is straightforward.
```python
>>> import pyarrow.compute as pc
>>> kommuner.schema
kommun_kod: string
kommun: string
geometry: binary
-- field metadata --
ARROW:extension:metadata: '{"crs":{"$schema":"https://proj.org/schemas/' + 1296
ARROW:extension:name: 'geoarrow.wkb'
-- schema metadata --
geo: '{"version":"1.1.0","primary_column":"geometry","columns":{"geometry' + 1621
# County code for Skåne is 12
>>> kommuner = kommuner.filter(pc.starts_with(pc.field("kommun_kod"), "12"))
>>> geojson = swemaps.table_to_geojson(kommuner)
```
You could also use list comprehension on the GeoJSON to filter it.
```python
>>> geojson["features"] = [
feature
for feature in geojson["features"]
if feature["properties"]["kommun_kod"].startswith("12")
]
```
Anyway, now we can plot Skåne.
```python
>>> skane = px.choropleth(
df,
geojson=geojson,
color="Value",
locations="Kommun",
featureidkey="properties.kommun",
projection="mercator",
color_continuous_scale="Viridis",
fitbounds="locations",
basemap_visible=False,
title="Skåne municipalities"
)
skane.show()
```
![skåne](assets/ex3.png)
### GeoPandas and plotnine
Another possibility is to load the GeoParquet into a GeoDataFrame.
```python
>>> import geopandas as gpd
>>> gdf = gpd.read_parquet(swemaps.get_path("lan"))
>>> gdf.head()
lan_kod lan geometry
0 01 Stockholms län MULTIPOLYGON (((17.24034 59.24219, 17.28475 59...
1 03 Uppsala län MULTIPOLYGON (((17.36606 59.61224, 17.35475 59...
2 04 Södermanlands län MULTIPOLYGON (((15.95815 58.96497, 15.8613 58....
3 05 Östergötlands län MULTIPOLYGON (((14.93369 58.13112, 14.89472 58...
4 06 Jönköpings län MULTIPOLYGON (((14.98311 57.9345, 15.00458 57....
# And with matplotlib installed as well we can have quick look
>>> gdf.plot()
```
![län](assets/ex4.png)
For best results with `plotnine` you can either reproject to SWEREF 99 TM or set the aspect ratio in `coord_fixed()`. A ratio of around 1.96 to 1.98 should be near optimal.
```python
>>> gdf = gpd.read_parquet(swemaps.get_path("kommun"))
# Insert some random values
>>> gdf["value"] = np.random.randint(1, 600, size=len(gdf["kommun"]))
# Reproject back to SWEREF 99 TM
>>> gdf = gdf.to_crs(epsg=3006)
>>> (
ggplot(gdf, aes(fill="value"))
+ geom_map(show_legend=False)
+ coord_fixed() # Or skip the reprojection above and set ratio manually here
+ scale_fill_cmap("YlGnBu")
+ theme(
axis_ticks=element_blank(),
panel_background=element_rect(fill="white"),
axis_text_x=element_blank(),
axis_text_y=element_blank(),
)
)
```
SWEREF 99 TM | WGS 84
:-------------------------:|:-------------------------:
![sweref99tm](assets/ex5.png) | ![wgs84](assets/ex6.png)
## Additional map data
Larger datasets including geometries for electoral districts, RegSO, and DeSO can be manually fetched. Once downloaded the files are cached for efficient reuse.
```python
>>> districts = swemaps.fetch_map("valdistrikt_2022")
>>> districts
PosixPath('/home/stefur/.cache/swemaps-data/v0.2.0/valdistrikt_2022.parquet')
```
Raw data
{
"_id": null,
"home_page": null,
"name": "swemaps",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "choropleth, geoarrow, geopandas, geoparquet, maps, pyarrow, sweden",
"author": null,
"author_email": "Stefan Furne <stefan@furne.net>",
"download_url": "https://files.pythonhosted.org/packages/cf/4d/125e42985874fd2f77b5ff3ac2b8b5c78d364012934a40a7bdbabc2a0a33/swemaps-0.2.1.tar.gz",
"platform": null,
"description": "# swemaps\n\nMaps of Sweden in [GeoParquet](https://github.com/opengeospatial/geoparquet) for easy usage. \n\nThe parquets have been created from files published by:\n- [Statistics Sweden](https://www.scb.se/hitta-statistik/regional-statistik-och-kartor/regionala-indelningar/) \n- [The Swedish Agency for Economic and Regional Growth](https://tillvaxtverket.se/tillvaxtverket/statistikochanalys/statistikomregionalutveckling/regionalaindelningar/faregioner.1799.html)\n- [Swedish Election Authority](https://www.val.se/valresultat/riksdag-region-och-kommun/2022/radata-och-statistik.html)\n\n The map data includes counties, municipalities, electoral districts and FA regions. The original geometries have been transformed from SWEREF 99 TM (EPSG:3006) to WGS 84 (EPSG:4326) for better out-of-the-box compatibility with interactive and web-based toolkits such as Folium and Plotly. The column names have also been somewhat sanitized (e.g. `KnKod` -> `kommun_kod`).\n\nThe package gets you the file path so that you can load it with your prefered tool, for example PyArrow or GeoPandas. An extra convenience function is included to quickly convert a PyArrow Table object to GeoJSON.\n\nMade for Python with inspiration from [swemaps2](https://github.com/filipwastberg/swemaps2). \n\n## Municipalities and counties\n\nMunicipalities | Counties\n:-------------------------:|:-------------------------:\n![municipalities](assets/ex1.png) | ![counties](assets/ex2.png)\n\n### PyArrow example with Plotly\n\n```python\n>>> import plotly.express as px\n>>> import pyarrow.parquet as pq\n>>> import swemaps\n\n# Load the map for the specified type\n>>> kommuner = pq.read_table(swemaps.get_path(\"kommun\"))\n\n>>> kommuner.column_names\n['kommun_kod', 'kommun', 'geometry']\n\n# The convenience function returns GeoJSON from a PyArrow table object\n>>> geojson = swemaps.table_to_geojson(kommuner)\n\n# Here's a dataframe with municipalities and some random values that we can plot\n>>> df.head()\nshape: (5, 2)\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Kommun \u2506 Value \u2502\n\u2502 --- \u2506 --- \u2502\n\u2502 str \u2506 i64 \u2502\n\u255e\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u256a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2561\n\u2502 Ale \u2506 544 \u2502\n\u2502 Alings\u00e5s \u2506 749 \u2502\n\u2502 Alvesta \u2506 771 \u2502\n\u2502 Aneby \u2506 241 \u2502\n\u2502 Arboga \u2506 763 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n# Use Plotly to create a choropleth using the dataframe and GeoJSON\n>>> fig = px.choropleth(\n df,\n geojson=geojson,\n color=\"Value\",\n locations=\"Kommun\",\n featureidkey=\"properties.kommun\",\n projection=\"mercator\",\n color_continuous_scale=\"Viridis\",\n fitbounds=\"locations\",\n basemap_visible=False,\n )\n```\n\nYou might want to subset the map of municipalities for a specific county or a group of counties. Since the geometry is loaded as a PyArrow table the filter operation is straightforward.\n\n```python\n>>> import pyarrow.compute as pc\n\n>>> kommuner.schema \n\nkommun_kod: string\nkommun: string\ngeometry: binary\n -- field metadata --\n ARROW:extension:metadata: '{\"crs\":{\"$schema\":\"https://proj.org/schemas/' + 1296\n ARROW:extension:name: 'geoarrow.wkb'\n-- schema metadata --\ngeo: '{\"version\":\"1.1.0\",\"primary_column\":\"geometry\",\"columns\":{\"geometry' + 1621\n\n# County code for Sk\u00e5ne is 12\n>>> kommuner = kommuner.filter(pc.starts_with(pc.field(\"kommun_kod\"), \"12\"))\n\n>>> geojson = swemaps.table_to_geojson(kommuner)\n```\n\nYou could also use list comprehension on the GeoJSON to filter it.\n\n```python\n>>> geojson[\"features\"] = [\n feature\n for feature in geojson[\"features\"]\n if feature[\"properties\"][\"kommun_kod\"].startswith(\"12\")\n ]\n```\n\nAnyway, now we can plot Sk\u00e5ne.\n```python\n>>> skane = px.choropleth(\n df,\n geojson=geojson,\n color=\"Value\",\n locations=\"Kommun\",\n featureidkey=\"properties.kommun\",\n projection=\"mercator\",\n color_continuous_scale=\"Viridis\",\n fitbounds=\"locations\",\n basemap_visible=False,\n title=\"Sk\u00e5ne municipalities\"\n )\n\nskane.show()\n```\n\n![sk\u00e5ne](assets/ex3.png)\n\n### GeoPandas and plotnine\n\nAnother possibility is to load the GeoParquet into a GeoDataFrame.\n\n```python\n>>> import geopandas as gpd\n\n>>> gdf = gpd.read_parquet(swemaps.get_path(\"lan\"))\n\n>>> gdf.head()\n\n lan_kod lan geometry\n0 01 Stockholms l\u00e4n MULTIPOLYGON (((17.24034 59.24219, 17.28475 59...\n1 03 Uppsala l\u00e4n MULTIPOLYGON (((17.36606 59.61224, 17.35475 59...\n2 04 S\u00f6dermanlands l\u00e4n MULTIPOLYGON (((15.95815 58.96497, 15.8613 58....\n3 05 \u00d6sterg\u00f6tlands l\u00e4n MULTIPOLYGON (((14.93369 58.13112, 14.89472 58...\n4 06 J\u00f6nk\u00f6pings l\u00e4n MULTIPOLYGON (((14.98311 57.9345, 15.00458 57....\n\n# And with matplotlib installed as well we can have quick look\n>>> gdf.plot()\n```\n\n![l\u00e4n](assets/ex4.png)\n\nFor best results with `plotnine` you can either reproject to SWEREF 99 TM or set the aspect ratio in `coord_fixed()`. A ratio of around 1.96 to 1.98 should be near optimal.\n\n```python\n>>> gdf = gpd.read_parquet(swemaps.get_path(\"kommun\"))\n\n# Insert some random values\n>>> gdf[\"value\"] = np.random.randint(1, 600, size=len(gdf[\"kommun\"]))\n\n# Reproject back to SWEREF 99 TM\n>>> gdf = gdf.to_crs(epsg=3006)\n\n>>> (\n ggplot(gdf, aes(fill=\"value\"))\n + geom_map(show_legend=False)\n + coord_fixed() # Or skip the reprojection above and set ratio manually here\n + scale_fill_cmap(\"YlGnBu\")\n + theme(\n axis_ticks=element_blank(),\n panel_background=element_rect(fill=\"white\"),\n axis_text_x=element_blank(),\n axis_text_y=element_blank(),\n )\n )\n```\n\nSWEREF 99 TM | WGS 84\n:-------------------------:|:-------------------------:\n![sweref99tm](assets/ex5.png) | ![wgs84](assets/ex6.png)\n\n\n## Additional map data\nLarger datasets including geometries for electoral districts, RegSO, and DeSO can be manually fetched. Once downloaded the files are cached for efficient reuse.\n\n```python\n>>> districts = swemaps.fetch_map(\"valdistrikt_2022\")\n>>> districts\nPosixPath('/home/stefur/.cache/swemaps-data/v0.2.0/valdistrikt_2022.parquet')\n```\n",
"bugtrack_url": null,
"license": null,
"summary": "Maps of Sweden in GeoParquet for easy usage.",
"version": "0.2.1",
"project_urls": {
"Repository": "https://github.com/stefur/swemaps"
},
"split_keywords": [
"choropleth",
" geoarrow",
" geopandas",
" geoparquet",
" maps",
" pyarrow",
" sweden"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "bcdb64c922ef103b0267420371f1ec83dae439a4c57b2df59b5855153cc6fd00",
"md5": "67ec27b4bb8511ed6de5d77a56a72810",
"sha256": "85a0754428d585b8c740598cc04d9598ec733d9868c234abd4eafbbff375c1ef"
},
"downloads": -1,
"filename": "swemaps-0.2.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "67ec27b4bb8511ed6de5d77a56a72810",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 244424,
"upload_time": "2024-10-20T16:19:30",
"upload_time_iso_8601": "2024-10-20T16:19:30.432501Z",
"url": "https://files.pythonhosted.org/packages/bc/db/64c922ef103b0267420371f1ec83dae439a4c57b2df59b5855153cc6fd00/swemaps-0.2.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "cf4d125e42985874fd2f77b5ff3ac2b8b5c78d364012934a40a7bdbabc2a0a33",
"md5": "58c40615e90763146da05eddd059e711",
"sha256": "41f6ed0aa6de0cd00ce2b45b73af7cbecc96ef37cf9bb4ad549e559496a8a71a"
},
"downloads": -1,
"filename": "swemaps-0.2.1.tar.gz",
"has_sig": false,
"md5_digest": "58c40615e90763146da05eddd059e711",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 816425,
"upload_time": "2024-10-20T16:19:32",
"upload_time_iso_8601": "2024-10-20T16:19:32.142168Z",
"url": "https://files.pythonhosted.org/packages/cf/4d/125e42985874fd2f77b5ff3ac2b8b5c78d364012934a40a7bdbabc2a0a33/swemaps-0.2.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-10-20 16:19:32",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "stefur",
"github_project": "swemaps",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "swemaps"
}