#30DayMapChallenge 2025
Posted by aselnigu on 30 November 2025 in English. Last updated on 1 December 2025.The #30DayMapChallenge is a social media event where map enthusiasts create daily maps based on a set theme throughout November. You can find more details on the website 30daymapchallenge.com.
After observing the challenge for many years, I participated for the first time in 2025.
I wanted to use the challenge to better understand fundamental mapping concepts. It wasn’t primarily about creating especially beautiful or eye-catching maps. Rather, I am working on a small project where I create vector tiles for small areas—mostly using free software and open formats like Shortbread and Versatiles. At the same time, I am increasingly experimenting with self-created vector tiles and custom styles. So far, I have used Tilekiln or Tilemaker for the tiles, and for styling, I used Glug for the first time. Since I also enjoy Leaflet, I include a Leaflet map wherever it fits better.
In this article, I want to experiment with my designs and work on questions that are still open in my to-do list, or simply try out something new.

01-11 – Points
Challenge Classic: Map with point data (e.g., individual locations, points of interest, clusters). Focus on effective symbolization and density visualization.
I started with ready-made Shortbread Tiles and a style that so far only includes waterways. Building on that, I added castles and ruins in the German style and, if available in the OpenStreetMap data, displayed their names directly underneath.
To do this, I first created my own sprites using basemaps-sprites, which I then reference in the style.json via "sprite": "http://127.0.0.1:8081/sprites":
{
"version": 8,
"name": "vwk",
"glyphs": "http://127.0.0.1:8081/fonts/{fontstack}/{range}.pbf",
"sprite": "http://127.0.0.1:8081/sprites",
"sources": { ... }
}
My goal was to figure out how to use custom icons. In the style, now these icons can be referenced via the icon-image property.

02-11 – Lines
Today I colored the streets on the map. In doing so, I again followed the openstreetmap-carto-de style. Even though I haven’t made much progress yet, the map is already noticeably more colorful.

I had assumed that openstreetmap-carto-de strictly follows the official German cartographic style. I’ll need to research this in more detail at some point. maps.rlp.de colors the streets differently, for example.
03-11 – Polygons
Challenge Classic: Create a map focused on area features (e.g., administrative regions, land use, boundaries). Use fills, patterns, and choropleth techniques.
Today I experimented with areas and started by rendering only forests relatively transparently, using the colors from the OpenStreetMap Carto project.

Areas are particularly challenging for me because you have to carefully consider which areas overlap, and how their transparency, color intensity, and stacking order affect the overall view. Even in a small, manageable area, this is already tricky – for the entire world, it seems almost impossible.
04-11 – Data challenge: My Data
Map something personal using your own dataset. Visualize GPS traces, your commute, or a unique, small dataset you created. (Need simple data? Try geojson.io)
My goal is to experiment with Vector Tiles. Today, out of curiosity, I explored how to integrate a GPX file into Vector Tiles. I imported the GPX track from today’s dog walk into my database using ogr2ogr and, based on that, converted each point into Vector Tiles.
For the style, I chose an SVG symbol showing footprints for each point. Where possible (sufficient space), the elevation is displayed next to the footprints. I wanted to try this out and learned how important it is to pay attention to projections when importing GPX data – in my case, ST_AsMVTGeom(ST_Transform(geom, 3857)) is crucial. Converting it beforehand would probably have been possible with ogr2ogr as well, but I hadn’t thought of it before.
SELECT
ST_AsMVTGeom(ST_Transform(geom, 3857), {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS geom,
ele
FROM trackpunkte
WHERE ST_Transform(geom, 3857) && {{bbox}}

05-11 – Earth
Classical Elements ¼: Focus on the tangible and grounded. Map landforms, geology, soil, agriculture, elevation, or anything solid beneath your feet.
Elevation and Contours are an important element for me on a map. Today, I explored how to integrate ST_Contour into Vector Tiles.

06-11 – Dimensions
Map beyond 2D. Visualize data using 3D models, extrusions (building heights), depth, time (as a dimension), or an unconventional multivariate approach.
Today, I am creating a style in which the buildings are given shadows – exclusively via the style, without any actual height data or floor numbers. For comparison, I have included the original version of the buildings without the shadow layer.


07-11 – Accessibility
Map how people (or things) get around. Visualize travel time, barriers, inclusive design, public transport reach, or create a map that is itself highly accessible to all users.
In my opinien, accessibility does not really exist—at best, we can strive for reduced barriers. Especially in maps, language plays a crucial role when it comes to accessibility. That is why I had a look how preferred language tags can be used.
For this purpose, I downloaded data around Seoul using
wget -O seoul.osm https://overpass-api.de/api/map?bbox=126.802826,37.467229,127.137222,37.641422
and continued working with it.
A German reader can probably assign the city name of the node 서울특별시 less easily in the next image than the label “Seoul” shown in the image after that. I achieved this by importing name:de where available, alternatively name:en, and using name (the local-language name) as a fallback.


I then took this one step further. Since OpenStreetMap often reflects a colorful mix of personal naming preferences, I considered how certain labels could be standardized to make them more understandable for everyone. I took inspiration for the rule from github.com/giggls/osml10n.
I started by abbreviating the word “Straße”, as it is frequently spelled in different ways in my country. In the first map, the street names are written out in full; in the second map, they are abbreviated in a consistent way.


08-11 – Urban
(World Urbanism Day) Map the built environment: dense street networks, highrises, urban sprawl, city infrastructure, or population density within a metro area.
Today, I examined the urban layout around Koblenz, paying particular attention to keeping the font size of city names proportional to the size of each city.

09-11 – Analog
Step away from the screen! Create your map using traditional methods (e.g., pen, pencil, paint, collage, physical models). Show the handmade process!
Today, I’m showing a map that was created to help locate specific trees in our forest.

This is how to locate the 7th path on site.

10-11 – Air
Classical Elements 2/4: Focus on the atmosphere. Map weather, wind patterns, air traffic, pollution, or airborne transmission (e.g., pollen, sound).
Today I tried something I probably wouldn’t have attempted without this challenge. While looking for inspiration, I came across the website openflightmaps.org/ and found the raster maps particularly interesting.

I took a closer look at the area around Koblenz.

Next, I considered how to integrate the data into my Vector Tiles project. For this, I imported OpenAir data for Germany into my database. I referred to the documentation in the file OpenAir_File_Format_Support.pdf for guidance.

In the PostGIS Geometry Viewer, I checked the first 100 table entries – everything looked good to me.

After that, I created the Vector Tiles. I didn’t style them further, but the MapLibre Inspector displays them correctly.

11-11 – Minimal map
Challenge yourself to use the fewest possible elements (color, line weight, labels) while keeping the map clear, useful, and informative.
On Day 11, I focused on the names of the waterways. I displayed the names of the rivers near Koblenz in larger font; when zooming in, the streams also appear.

This reminded me of how, many years ago, I learned which parts make up the Rhenish Massif.

12-11 – Map from 2125
How will maps look 100 years from now? Create a speculative map of what might be (or what you hope will be).
In populated areas, I would like to have only bike paths. I have already learned how to color the streets. For the bike paths, I think green is the most suitable color.

13-11 – 10 minute map
Start the timer! The maximum allowed time to design and produce this map is 10 minutes. Focus on speed, simplicity, and core communication.
This month, I’m waiting for LeafletJs 2.0 to be released. Today, I took another look at the alpha version. In the time I had, I only managed to add a marker, a circle, and a triangle like in the official exampels. This time, I created a link to the map.

14-11 – Data challenge: OpenStreetMap
Use OpenStreetMap (OSM) data as your primary source. Map your favorite feature, contribute back to the project, or style the map in an interesting way.
On my daily dog walk, two newly installed fixed hammocks have recently appeared. More and more of them can now be found along the Rheinsteig trail.

Today, I met a hiker who was actually looking for these hammocks. That gave me the idea to add them to OpenStreetMap and make them visible in my project.

15-11 – Fire
Classical Elements ¾: Focus on energy, light, or transformation. Map heat, wildfires, energy consumption, population density at night, or volcanic activity.
I became interested in the power grid in Germany and read up on it in the Wiki. Then, I queried all objects with the tag https://wiki.openstreetmap.org/wiki/Key:power using Overpass:
[out:json][timeout:25];
(
node["power"]({{bbox}});
way["power"]({{bbox}});
relation["power"]({{bbox}});
);
out geom;
![#30DayMapChallenge 2025]https://astrid-guenther.de/_astro/30dmc_2025_day15.CrYkXw2S_pOPVm.webp)
The vector tiles initially did not include the power tag. Therefore, I loaded this data into a PostGIS database using osm2pgsql and Themepark. Based on this, I created SQL queries for vector tiles so that the power network features are available from zoom level 7. I will likely increase the minimum zoom level later.
SELECT
ST_AsMVTGeom(
geom,
{{ unbuffered_bbox }},
{{ extent }},
{{ buffer }}
) AS way,
node_id AS id,
power,
ref,
voltage,
operator
FROM power_nodes
WHERE geom && {{ bbox }}
AND {{ zoom }} >= 7

For styling, I started with colored points. For a more visual representation, I plan to use a power pole icon.
layer(:power_nodes, source: :vwk, source_layer: :power_nodes, type: 'circle') {
circle_color match(get(:power),
'pole', '#1f78b4',
'tower', '#e31a1c',
'#888'
)
circle_radius interpolate([:linear], get(:voltage),
0, 1,
110, 2,
220, 3,
380, 4
)
minzoom 7
}
I also learned that a pole is a small wooden mast, while a tower is a large high-voltage pylon.

16-11 – Cell
Map something composed of small, discrete units or networks. This could be a geographic cell (raster, tessellation), a cellular network, or a biological/social process (e.g., disease spread).
In my view, a castle complex can be mapped as a network of discrete units: walls, historical POIs, paths, towers, buildings, vegetation, courtyards, and visitor facilities can each be represented as individual elements. The raw OSM data for Marksburg show me how much effort it takes to map even a small area in a way that remains clearly visible at all zoom levels.

17-11 – A new tool
Challenge Classic: Experimentation is key! Create your map using a software, language, library, or technique you have never used before.
In my to-do list, I had Switching a style to glug. So I’m killing two birds with one stone: I’m ticking off a to-do item and moving one day forward in the challenge.
At the same time, I save lines of code. My version of the style for contour lines, which uses https://github.com/unvt/charites, is significantly longer.
id: contour-lines
type: line
source: vwk
source-layer: contours
minzoom: 10
paint:
line-color:
- interpolate
- [linear]
- [get, elevation]
- 0
- "#f2f0f7"
- 500
- "#cbc9e2"
- 1000
- "#9e9ac8"
- 1500
- "#756bb1"
- 2000
- "#54278f"
line-width:
- interpolate
- [linear]
- [zoom]
- 10
- 0.3
- 14
- 1
- 16
- 1.5
id: contour-labels
type: symbol
source: vwk
source-layer: contours
minzoom: 14
layout:
text-field:
- to-string
- [get, elevation]
text-font:
- !!inc/file style/inc/regular-font.yaml
text-size: 10
symbol-placement: line
paint:
text-color: "#444"
text-halo-color: "#fff"
text-halo-width: 1
I don’t know Ruby, but I was able to create this syntax:
layer(:contour_lines, source: :vwk, source_layer: :contours, type: 'line') {
line_color interpolate([:linear],
get(:elevation),
0, '#f2f0f7',
500, '#cbc9e2',
1000, '#9e9ac8',
1500, '#756bb1',
2000, '#54278f'
)
line_width interpolate([:linear], zoom,
10, 0.3,
14, 1,
16, 1.5
)
}
layer(:contour_labels, source: :vwk, source_layer: :contours, type: 'symbol') {
symbol_placement :line
text_field to_string(get(:elevation))
text_font literal(:'Noto Sans Regular')
text_size interpolate([:linear], zoom,
13, 0,
14, 10
)
text_color '#444'
text_halo_color '#fff'
text_halo_width 1
}
And the contour lines look the same as before.

18-11 – Out of this world
Map something non-Earth: a fantasy realm, an exoplanet, the Moon, Mars, deep space, or a purely conceptual place (e.g., a mental map).
Today’s topic reminded me of crs-simple. CRS stands for Coordinate Reference System. I’ve used it several times for ImageMaps before. Here, I’m using a photo of the Marksburg and starting with a simple marker. If I find the time, I plan to describe the individual parts of the castle in more detail as you zoom in.

19-11 – Projections
(GIS Day) Focus entirely on map projections. Choose an unusual or misunderstood projection to highlight a theme, or visualize distortion. (See xkcd.com/977)
I understand that the surface of a ball cannot be simply displayed on a flat sheet of paper. I also realize that the Earth is more complicated than a ball. I enjoy looking at projections in more detail. My favorite is the Bonne projection.
All of this is quite complicated, so I only ventured the following comparison: equal.bbox.earth/leaflet-eq2merc/.

What I learned this summer: Mercator is angle-preserving and maps the Earth onto a rectangle, which makes navigation easier and allows the map to be easily divided into tiles, but it strongly distorts areas at high latitudes, whereas Equal Earth preserves areas and provides more realistic comparisons between countries and continents.
20-11 – Water
Classical Elements 4/4: Focus on the fluid. Map hydrology, oceans, currents, water accessibility, sea level rise, precipitation, or anything aquatic.
Today I added the ocean layer.

For this, I used data from osmdata.openstreetmap.de. The next two images show the difference between the simplified version, used at low zoom levels, and the more detailed version for closer views.


21-11 – Icons
Create a map where icons, pictograms, or custom symbols are the main focus. Use them to highlight points of interest or replace traditional cartographic features.
On Day 14, I had already added a custom symbol. Today, I added more elements in the same way. I will probably display the guideposts only at more detailed zoom levels later.

22-11 – Data challenge: Natural Earth
Use the Natural Earth dataset as your primary source for a visually stunning small-scale world or continent map.
I’m using the file ne_110m_admin_0_boundary_lines_land.zip. The “110m” refers to the scale (1:110,000,000) — so it’s a very undetailed global representation.
Contents of the file: It contains vector data with lines (“boundary lines”) representing Admin 0 boundaries (i.e., country borders) on land. The “on land” part was initially a bit confusing. I worried that I wouldn’t get all the data, but once water is added, everything looks fine.
Usefulness: Ideal for world maps with low detail or overview maps — perfect for low zoom levels in vector tiles. For more detailed views, other data sources can be used.

I had actually planned to color all countries differently today. However, the boundaries weren’t suitable for that. I need polygons, such as those available here: https://www.naturalearthdata.com/downloads/10m-cultural-vectors/10m-admin-0-countries/. I might do this next year.
23-11 – Process
Show how you make a map. This could be a tutorial, a step-by-step graphic, a blog post, a video, or a screenshot of your work environment. Combine it with a map from another day!
Here is a step-by-step guide showing how I added cliffs to the database, the vector tiles, and the style on Day 27.

- In Lua and osm2pgsql
First, I load the data from OpenStreetMap into a PostGIS database using Themepark. The script checks whether a cliff is mapped as a line or a polygon. Polygons are converted into lines so that they can be displayed consistently in the tiles.
themepark:add_table{
name = 'land_cliff',
ids_type = 'way',
geom = 'linestring',
columns = themepark:columns({
{ column = 'kind', type = 'text', not_null = true },
{ column = 'minzoom', type = 'int', not_null = true, tiles = 'minzoom' },
}),
tiles = {
minzoom = 11
},
expire = expire.vwk(11, 14, 'land_cliff', 'full-area')
}
themepark:add_proc('way', function(object, data)
if object.tags.natural == 'cliff' then
local geom = object:as_linestring()
if not geom then
local poly = object:as_area()
if poly then
geom = poly:boundary()
end
end
if not geom then return end
local a = { geom = geom, kind = 'cliff', minzoom = 11 }
themepark:insert('land_cliff', a, object.tags)
end
end)

- SQL-Abfrage for Vector Tiles
The query converts the lines using ST_AsMVTGeom so that they can be used as vector tiles in Mapbox/MapLibre. It also filters by minzoom.
SELECT
ST_AsMVTGeom(
geom,
{{ unbuffered_bbox }},
{{ extent }},
{{ buffer }}
) AS way,
kind
FROM land_cliff
WHERE kind = 'cliff'
AND geom && {{ bbox }}
AND {{ zoom }} >= minzoom
- Styling the cliffs
For visualization, there are two layers:
land_cliffdisplays the cliff lines, with the line width adapting to the zoom level.cliff_trianglesplaces small triangles along the line to visually emphasize the steepness.
layer(:land_cliff, source: :vwk, source_layer: :land_cliff) {
filter kind == 'cliff'
line_color '#a0a0a0'
line_width interpolate([:linear], zoom, 11, 1, 15, 2)
line_cap 'round'
line_join 'round'
sort_key 100
}
layer(:cliff_triangles, source: :vwk, source_layer: :land_cliff, type: "symbol") {
filter kind == "cliff"
symbol_placement "line"
symbol_spacing 12
icon_allow_overlap true
icon_image "cliff"
icon_size interpolate([:linear], zoom,
12, 0.8,
14, 1.0,
16, 1.1
)
icon_color "#dcdcdc"
symbol_sort_key 31
}
Now the cliffs are clearly recognizable and can be rendered as vector tiles. The result is shown in the image for Day 27.
24-11 – Places and their names
Focus on toponymy (place names). Experiment with font choices, label placement, typography, multiple languages, or the history and meaning behind a name.
Today I experimented with font size in relation to the size of the city. As the typeface itself, I like the Noto Fonts.

25-11 – Hexagons
Challenge Classic: Use hexagonal binning (hexbins) or a hexagonal grid system to visualize your data. Celebrate this beautiful and efficient tessellation!
Today I took my first look at the function https://postgis.net/docs/ST_HexagonGrid.html. I used buildings as an example.
First, I show the map section with the original building geometries.

In the next image, the hexagons are shown: areas with many buildings are displayed darker than those with only a few buildings.

I also add my code snippets below:
WITH hex AS (
SELECT (ST_HexagonGrid(50, {{unbuffered_bbox}}::geometry)).geom AS hexgeom
),
binned AS (
SELECT
h.hexgeom,
COUNT(b.geom) AS building_count
FROM hex h
LEFT JOIN buildings b
ON ST_Intersects(b.geom, h.hexgeom)
GROUP BY h.hexgeom
)
SELECT
ST_AsMVTGeom(hexgeom, {{unbuffered_bbox}}, {{extent}}, {{buffer}}) AS geom,
building_count
FROM binned
WHERE hexgeom && {{bbox}}
and
layer(:building_fill, source: :vwk, source_layer: :buildings, type: 'fill') {
fill_color let('count', building_count) <<
interpolate([:linear], var('count'),
0, to_color("#f7fbff"),
20, to_color("#08519c")
)
}
26-11 – Transport
(World Sustainable Transport Day) Map mobility, traffic flow, public transit networks, logistics chains, or advocate for sustainable transport options.
While exploring the “public_transport” layer, I noticed that the Rhine (on the right in the image) has more ferry connections than the Moselle (on the left). The Moselle, on the other hand, has more bridges.

27-11 – Boundaries
Map lines of division—political, physical, ecological, or conceptual. Explore the meaning and impact of a dividing line, real or perceived.
When I first thought about boundaries and barriers, I was considering the elements shown in openstreetmap-carto-de using the symbols under symbols/barrier.
During a tour of the only hilltop castle on the Middle Rhine that was never destroyed and preserved, I realized that elevation differences and rugged terrain can represent much stronger barriers. Therefore, I included cliffs in my project. In the image, you can see the cliff that likely helped protect the Marksburg so effectively.

28-11 – Black
(Black Friday) Interpret the theme of Black. The map can be purely monochromatic, represent absence/darkness (e.g., light pollution), or relate to themes of consumption.
Today, I converted everything to grey. Compared to the colour version, I am amazed at how many things on the map can still be clearly identified. The water or the river is obvious to me, even though it is not drawn in blue.


29-11 – Raster
Challenge Classic: Map using raster data. Focus on satellite imagery, elevation models (DEMs), land cover, or pixel-based art.
I had already looked into how I could integrate ST_Contour into Vector Tiles. Today, I experimented a little with the style and found it exciting to illustrate the Rhine River using only contour lines.

30-11 – Makeover
Take a map you made during the month or an older piece and redesign it. Focus on improving the aesthetics, clarity, or data communication.
The road crossings at intersections have been a cause for concern up to now. I looked into this today and found a solution.


Now I have many new ideas and quite a jumble of unfinished concepts in my project, and it will probably take me a year to sort everything out.
Discussion