Skip to content

OpenRailwayMap-vector integration - #132

Open
hiddewie wants to merge 791 commits into
OpenRailwayMap:masterfrom
hiddewie:master
Open

OpenRailwayMap-vector integration#132
hiddewie wants to merge 791 commits into
OpenRailwayMap:masterfrom
hiddewie:master

Conversation

@hiddewie

@hiddewie hiddewie commented Jun 7, 2025

Copy link
Copy Markdown
Contributor

Hi!

Over the past years I have been working on a fork of the OpenRailwayMap.

The announcement can be found on the OpenRailwayMap mailing list and on the OpenStreetMap community forum.

The fork has several features, and is actively being developed by me and several more contributors:

  • Vector-based data: the map is interactive with e.g. hover and popup showing data details.
  • Map style improvements: station size, state of lines and statons, signal support for many new countries.
  • Visual UI improvements like: configurable background map, map settings and dark mode.
  • Additional layers: loading gauge and track class.
  • Daily data updates for the entire planet.
  • OpenHistoricalMap integration for historical railway infrastructure.

At the moment the fork is hosted on https://openrailwaymap.app using Fly.io. This is purely practical, it could be hosted anywhere else.

This pull request is a starter to discuss:

  • Can we integrate the fork into the Github OpenRailwayMap organization
  • Can we publish the fork on the domain openrailwaymap.org?

These are open questions, any response is appreciated!

Anasmasuris and others added 29 commits November 22, 2025 18:28
Recently, I've surveyed Warsaw Central with its new railway signalling
and having discussed at forum on Skyscrapercity
(https://www.skyscrapercity.com/threads/warszawa-warszawski-w%C4%99ze%C5%82-kolejowy.979536/page-364?post_id=195487518#post-195487518)
I've realised that a certain type of semaphore hadn't existed.

It's the case when semaphore has 5 cells
(green-yellow-red-yellow-white), but the 2-cell signals contain only
yellow lights, no green-yellow (as it's currently implied by code).
Thus, I've made it possible to render properly currently 2 semaphores
(G1C and G2C, https://www.openstreetmap.org/node/12021271222 and
https://www.openstreetmap.org/node respectively) at Warsaw Central
station

---------

Co-authored-by: Hidde Wieringa <hidde@hiddewieringa.nl>
Part of #662

Especially useful for yards which can be tagged as a `landuse = railway`
in combination with the area of the yard.

The station bubble automatically encloses the entire yard.

On Waycross, Rice Yard,
(http://localhost:8000/#view=13.86/31.18399/-82.3864):
<img width="1318" height="982" alt="image"
src="https://github.com/user-attachments/assets/c1c559b8-3b99-4543-85bb-cc7a639d6e10"
/>
Base implementation for some tagged US and Canada main and distant
signals

Closes #653

This will need more work as the US and Canada establish tagging
guidelines.

### US

Around Chicago

(http://localhost:8000/#view=14.3/41.89746/-87.92877&style=signals):
<img width="1755" height="574" alt="image"
src="https://github.com/user-attachments/assets/afffbc16-86b7-4891-a45d-fc85028ea20a"
/>

(http://localhost:8000/#view=15.64/41.482924/-88.088233&style=signals):
<img width="804" height="905" alt="image"
src="https://github.com/user-attachments/assets/55cc3340-f3cd-4848-ae45-fcb612a0019b"
/>

(http://localhost:8000/#view=16.76/38.680231/-90.172901&style=signals):
<img width="854" height="560" alt="image"
src="https://github.com/user-attachments/assets/a058616e-0c8f-4b07-8d68-4e5e55bbe1a7"
/>



### Canada

(http://localhost:8000/#view=14.42/44.59721/-75.69216&style=signals):
<img width="797" height="562" alt="image"
src="https://github.com/user-attachments/assets/f65db79a-bc0d-4e95-a53c-94a2265fc419"
/>
Fixes #681

For some reason all paragraph properties (those with a large amount of
text) were excluded from showing in the popup.

<img width="519" height="483" alt="image"
src="https://github.com/user-attachments/assets/cee9b30e-648c-4848-a806-3bd6e1241e66"
/>
<img width="995" height="572" alt="image"
src="https://github.com/user-attachments/assets/af7def20-7bcc-4446-8f2b-65933b38628d"
/>
This adjusts the colour scale of the speed layer with the goal of using
a wider range of colours for low speed values. See #668 for more
details.
<img width="1567" height="619" alt="image"
src="https://github.com/user-attachments/assets/6750670a-becb-4378-a071-120cec1bfdcf"
/>
Part of #662

Analysis in
#662 (comment)

This pull request uses the total rail length of all railway lines within
a yard, to assign a measure of importance to the yard.

This is similar to how platforms and railway routes assign importance to
passenger railway stations.

The current thresholds are set at >= 7200 meters of rail is a large
yard, >= 750 meters of rail is a normal yard, and all others are small.

I renamed everything *route count* to *importance*, because stations and
yards each get a (scaled) importance which can be compared (e.g. in API
search results).
Closes #555

## Goal

Improve the distribution of stations on the map for lower zoom levels,
worldwide, without cluttering the map with too many stations.

## Implementation and documentation

Method: *Discrete Isolation* to provide a measure of local importance of
a station, compared to a global importance.

See:
- https://osm2pgsql.org/doc/manual.html#strategy-discrete-isolation
-
https://blog.jochentopf.com/2022-12-19-selecting-settlements-to-display.html
- https://dx.doi.org/10.1007/s42489-021-00079-y

## Changes

The *Discrete Isolation* implementation of Osm2pgsql is used. This is
experimental but works fine.

After the station importance has been determined by the number of routes
or rail length (for yards), the discrete isolation algorithm is ran to
determine the discrete isolation column values for each station.

The discrete isolation are used to filter zooms 4 until 7, instead of
just the station importance values. The result is that huge stations
close together (e.g. in the same city) do not all show on the map in the
same location, because locally one of the stations will be the most
important.

Worldwide this makes a difference because many stations do not have many
routes like the large European stations, but do have more routes tagged
than other stations in the area, making them locally more important.

Furthermore, the requirement to have a railway reference has been
removed. For non-European stations, railway references are often not
tagged, which removed them from the map for the low zoom levels.

Zooms 3 and lower are unchanged and show no stations.

Zoom 4 shows medium and large stations without their references/names,
filtered by local importance for zoom 4.

Zoom 5 shows medium and large stations, filtered by local importance for
zoom 5.

Zoom 6 shows medium and large stations, filtered by local importance for
zoom 6.

Zoom 7 shows all stations, filtered by local importance for zoom 7, but
no references/names for small stations.

Zoom 8 and higher shows all stations.

## Testing

Image below: master branch, https://openrailwaymap.app

Image above: this branch, http://localhost:8000

### Europe

Zoom 4 (https://openrailwaymap.app/#view=4/51.14/18.47):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/8e385e5a-c797-4ac0-b603-71fa18da1b8a"
/>

Zoom 5 (https://openrailwaymap.app/#view=5/50.88/12.49):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/275f9a50-8200-44c2-b8b9-e944308e4ceb"
/>

Zoom 6 (https://openrailwaymap.app/#view=6/49.388/7.588):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/617b168b-2187-4802-a9ce-a466489e321b"
/>

Zoom 7 (https://openrailwaymap.app/#view=7/47.657/7.751):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/8ea468da-df71-469a-a209-234e721ff02d"
/>

### East Asia

Zoom 4 (https://openrailwaymap.app/#view=4/39.06/127.17):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/bba03360-9d9e-482d-a4f4-928a32890c36"
/>

Zoom 5 (https://openrailwaymap.app/#view=5/38.49/134.36):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/b9b18f3f-c7e7-41ef-b390-8a88850cdd5e"
/>

Zoom 6 (https://openrailwaymap.app/#view=6/35.741/132.211):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/4ee196a3-ac26-478c-8e1e-f1ba8d9efceb"
/>


Zoom 7 (https://openrailwaymap.app/#view=7/34.752/134.117):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/08c3939d-a794-44cd-9a88-8ca1b74426e8"
/>


### South Asia

Zoom 4 (https://openrailwaymap.app/#view=4/19.97/86.77):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/f6a6cf0e-0dc3-4386-8bd7-fdcc46297fe8"
/>

Zoom 5 (https://openrailwaymap.app/#view=5/21.78/82.24):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/0d9307f8-31d6-420e-be3a-baa095c391b4"
/>

Zoom 6 (https://openrailwaymap.app/#view=6/25.757/80.629):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/8b919f40-d221-41e8-b3c7-4934a04e217a"
/>

Zoom 7 (https://openrailwaymap.app/#view=7/22.007/74.38):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/487925aa-cc8f-4e80-9da3-3dc55c8625f1"
/>

### South America

Zoom 4 (https://openrailwaymap.app/#view=4/-34.87/-58.21):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/5e873a01-7972-4bfb-91cd-35b555c687f1"
/>

Zoom 5 (https://openrailwaymap.app/#view=5/-34.65/-62.82):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/74b72599-171a-4524-bb57-bebf0f906f21"
/>

Zoom 6 (https://openrailwaymap.app/#view=6/-33.533/-61.109):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/3495ceed-41de-47cd-ab70-2a4329c5eda3"
/>

Zoom 7 (https://openrailwaymap.app/#view=7/-34.062/-60.466):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/d2daa8d4-7a4a-4454-b0b9-e028391ff190"
/>

### North America

Zoom 4 (https://openrailwaymap.app/#view=4/38.16/-88.29):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/e332bcd7-64d2-4cac-b69e-5ca2697daffd"
/>

Zoom 5 (https://openrailwaymap.app/#view=5/39.85/-80.27):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/c681be9a-9517-4600-84b5-b826457bc3fb"
/>

Zoom 6 (https://openrailwaymap.app/#view=6/42.238/-83.259):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/975dbfed-8302-4c95-b230-39b0fb4ee0cd"
/>

Zoom 7 (https://openrailwaymap.app/#view=7/41.893/-85.488):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/dcf14865-3ac3-4b96-b83c-73a1fa9d151b"
/>

### Africa

Zoom 4 (https://openrailwaymap.app/#view=4/-27.11/29.33):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/cbc5a110-4c12-4f6d-897b-1f00ff3ce8da"
/>

Zoom 5 (https://openrailwaymap.app/#view=5/-29.81/25.02):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/012b08e7-689f-4392-8542-7a42e09e4709"
/>

Zoom 6 (https://openrailwaymap.app/#view=6/-27.548/28.778):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/b5409dc7-bcad-46fe-8e98-e4f67ebdb600"
/>

Zoom 7 (https://openrailwaymap.app/#view=7/-26.916/28.831):
<img width="1227" height="1395" alt="image"
src="https://github.com/user-attachments/assets/85f95c35-3ae3-4f01-81ab-d48252f9f147"
/>

## Future

Stations should not be rendered for low zooms if they are not on
main/branch lines, and for non-train modalities. There is much missing
tagging in some regions of the world. See e.g.
#663.

With more stop areas and routes tagged, the importance of a station is
clearer to determine worldwide. Some future tweaking of the discrete
isolation output values might be needed.
…ge (#687)

Japan (http://localhost:8000/#view=12.31/35.36436/139.54215):
<img width="506" height="427" alt="image"
src="https://github.com/user-attachments/assets/faa3effb-eafd-4fa4-9cfe-124fb778a8e9"
/>

Using flags for is not entirely correct (country != language), but works
well enough.

Adds emoji flag translations for:
- Japanese
- Chinese
- Farsi 
- Ukranian
- Georgian
- Armenian
- Hebrew
Closes #662 

Yard nodes which are contained in a landuse=railway area, assume the
landuse area as yard geometry.

Requires full planet data re-import.

Example: https://www.openstreetmap.org/node/9196070580

Before, after:
<img width="427" height="622" alt="image"
src="https://github.com/user-attachments/assets/46fa57e6-8fc3-49e9-bda0-ea47552aaba3"
/>
<img width="427" height="622" alt="image"
src="https://github.com/user-attachments/assets/1fec9c6c-1114-4047-a9f7-ff788ea8a138"
/>
For #409

For the station layers that contain names, provide a `lang` query
parameter.

More ideal would be the `Accept-Language` header that every browser
sends, but the cache is not varied across that header (Cloudflare
Enterprise feature).


<img width="693" height="403" alt="image"
src="https://github.com/user-attachments/assets/616a5389-e1cf-4da6-ada9-bd12db6096c0"
/>

<img width="604" height="366" alt="image"
src="https://github.com/user-attachments/assets/c9b17c49-8f12-47ca-bf72-920c76244348"
/>

low zooms, Amsterdam Centraal
(http://localhost:8000/#view=5.59/52.132/8.208):
<img width="323" height="189" alt="image"
src="https://github.com/user-attachments/assets/06c42782-1153-4a34-9373-ed12567cdcb7"
/>

Popup always shows the localized name, and the local name if not equal:
<img width="323" height="189" alt="image"
src="https://github.com/user-attachments/assets/e6655646-69fe-4efe-bd8a-e3df4f1b0da8"
/>
Fixes #477

Around Bodensee, Lindau
(http://localhost:8000/#view=14.94/47.54131/9.68011&date=1887):
<img width="1431" height="1111" alt="image"
src="https://github.com/user-attachments/assets/218003ac-08c1-4705-9a4c-dd5c4a15fa81"
/>
Fixes #409

Berlin Ostbahnhof:
<img width="1432" height="605" alt="image"
src="https://github.com/user-attachments/assets/2bc5d8dd-488a-40ed-8723-3fa081bf17d2"
/>

Tokyo (http://localhost:8000/#view=10.73/35.6533/139.7914):
<img width="1430" height="605" alt="image"
src="https://github.com/user-attachments/assets/75932d20-2452-4d1e-b349-15c875a0b2f4"
/>
<img width="773" height="482" alt="image"
src="https://github.com/user-attachments/assets/62f530b6-ae8c-4cbb-8b3e-14c5568abf91"
/>
Fixes #692

This will send a user agent header of the form
```
OpenRailwayMap API (https://openrailwaymap.app), httpx 0.28.1, Python 3.13.9 (main, Oct 15 2025, 16:47:14) [GCC 14.2.0]
```

Now it works
```shell
http http://localhost:8000/api/wikidata/Q567336

HTTP/1.1 307 Temporary Redirect
Cache-Control: public, max-age=0, stale-if-error=0
Connection: keep-alive
Content-Length: 0
Date: Sun, 30 Nov 2025 14:46:38 GMT
Server: nginx/1.29.3
location: https://upload.wikimedia.org/wikipedia/commons/thumb/8/81/Bahnhof_Berlin-Wannsee_Empfangsgebaeude_04-2015.jpg/330px-Bahnhof_Berlin-Wannsee_Empfangsgebaeude_04-2015.jpg
```

Popup also shows images again
<img width="657" height="543" alt="image"
src="https://github.com/user-attachments/assets/79ccb99d-8de1-4591-9aba-27bda2cfa23a"
/>
See
https://github.com/hiddewie/OpenRailwayMap-vector/actions/runs/19800992241/job/56728231479

Update is failing because of missing geometries during import. Possibly
due to faulty update.
Part of #683 

Implements the Turbo color scheme

See
https://research.google/blog/turbo-an-improved-rainbow-colormap-for-visualization/
See
https://gist.github.com/mikhailov-work/ee72ba4191942acecc03fe6da94fc73f?permalink_comment_id=3708728#gistcomment-3708728
See #668

The speed exponent has been set to 0.8 from the previous 0.63.

Color changes:
- Low speeds become darker
- Low speeds are different blue shades
- There is less purple
- Very high speeds become dark red

## Testing


### Legend

Left: before, right: after

<img width="214" height="717" alt="image"
src="https://github.com/user-attachments/assets/278244e0-c670-46e6-b5d1-f10edb176151"
/><img width="211" height="695" alt="image"
src="https://github.com/user-attachments/assets/6fedfba1-fec1-4432-b49e-27df5da747ad"
/>


### Worldwide

http://localhost:8000/#view=1.59/43.9/12.5&style=speed

Before 
<img width="1432" height="1115" alt="image"
src="https://github.com/user-attachments/assets/dac57a34-9abf-40f3-8491-c2bd6d2515fe"
/>

After
<img width="1431" height="1114" alt="image"
src="https://github.com/user-attachments/assets/b788b874-c3c5-474a-a875-0368ad0722d8"
/>


### Europe

http://localhost:8000/#view=4.59/48.91/12.68&style=speed

Before
<img width="1432" height="1115" alt="image"
src="https://github.com/user-attachments/assets/726a4809-4225-48b3-b876-39d0326276e6"
/>

After
<img width="1431" height="1114" alt="image"
src="https://github.com/user-attachments/assets/ddb5cc50-b330-40de-9c30-18bd8b0bc4d2"
/>


### Paris

http://localhost:8000/#view=8.27/48.69/3.046&style=speed

Before
<img width="1432" height="1115" alt="image"
src="https://github.com/user-attachments/assets/eb3ea28c-8fcd-4b10-9f72-5aab0100cc8c"
/>

After
<img width="1431" height="1114" alt="image"
src="https://github.com/user-attachments/assets/ea9f1e93-0539-465f-965c-5008dc03d4ff"
/>

### Toledo

http://localhost:8000/#view=10.73/39.9717/-3.8272&style=speed

Before 
<img width="1431" height="1114" alt="image"
src="https://github.com/user-attachments/assets/d427a560-6e53-4f5a-9ec8-a7e2d1166ba6"
/>


After
<img width="1431" height="1114" alt="image"
src="https://github.com/user-attachments/assets/87faadef-508d-4bdf-90c9-58a4afb848b4"
/>


### North America

http://localhost:8000/#view=5.77/42.178/-80.814&style=speed

Before
<img width="1432" height="1115" alt="image"
src="https://github.com/user-attachments/assets/de4672c7-2c54-4a14-a398-0d3eae22a33d"
/>

After
<img width="1431" height="1114" alt="image"
src="https://github.com/user-attachments/assets/2541917b-8520-4fc2-a8a4-eef297dda234"
/>


### Philadelphia

http://localhost:8000/#view=9.65/40.2206/-74.7075&style=speed

Before
<img width="1432" height="1115" alt="image"
src="https://github.com/user-attachments/assets/f62e6cba-8c04-401a-b799-ab1c91cca90c"
/>

After
<img width="1431" height="1114" alt="image"
src="https://github.com/user-attachments/assets/9b5bdb6f-0c0f-424d-8869-131b7d3bc256"
/>

### East Asia

http://localhost:8000/#view=4.32/36.68/124.51&style=speed

Before
<img width="1432" height="1115" alt="image"
src="https://github.com/user-attachments/assets/5f3dd3bf-e6c8-433a-8337-eab0c1348433"
/>


After
<img width="1431" height="1114" alt="image"
src="https://github.com/user-attachments/assets/013a95a2-3b24-49be-b966-c6797007331a"
/>

### Wuhan

http://localhost:8000/#view=7.22/31.253/114.225&style=speed

Before
<img width="1432" height="1115" alt="image"
src="https://github.com/user-attachments/assets/21458b4b-53b0-44fa-b05b-2d6c883886e8"
/>

After
<img width="1431" height="1114" alt="image"
src="https://github.com/user-attachments/assets/8e141590-0851-4952-93a3-823c9e83a20b"
/>
OSM data has been updated.
thanks to #580, we can now enlarge the `main` signal icons, which are
currently tiny. And add a different icon depending on
`railway:signal:main:shape`

also added all the other signs
([osmwiki](https://osm.wiki/New_Zealand/Railways))

<img width="105" height="71" alt="image"
src="https://github.com/user-attachments/assets/1ed8e729-7bfe-4e72-9960-ece4cea70ae8"
/>
<img width="272" height="110" alt="image"
src="https://github.com/user-attachments/assets/b03f37a1-c626-4ab4-a8a0-3d16526cf178"
/>
<img width="71" height="74" alt="image"
src="https://github.com/user-attachments/assets/2c3c1df8-301b-485f-b255-7298ffbf6fba"
/>
<img width="116" height="88" alt="image"
src="https://github.com/user-attachments/assets/3cc34a4a-ead9-40b1-aa11-50bef6cf3e94"
/>
<img width="187" height="224" alt="image"
src="https://github.com/user-attachments/assets/29c9d783-178a-477e-bb48-06d950ea8d5f"
/>
<img width="94" height="85" alt="image"
src="https://github.com/user-attachments/assets/4e227c7d-239a-44b7-81ad-4a96ee14a332"
/>
<img width="116" height="128" alt="image"
src="https://github.com/user-attachments/assets/feb8dac4-63cc-45e4-be49-1ec493245615"
/>
<img width="80" height="118" alt="image"
src="https://github.com/user-attachments/assets/0eb50af6-b07d-40f6-accd-32c0844b1f3b"
/>
<img width="79" height="59" alt="image"
src="https://github.com/user-attachments/assets/5f7a57a7-19ec-4a01-a1da-27fab4644357"
/>
<img width="52" height="54" alt="image"
src="https://github.com/user-attachments/assets/e7708b15-cba5-48a4-9a59-8a2ae4701f86"
/>
<img width="89" height="80" alt="image"
src="https://github.com/user-attachments/assets/0fd701bc-78ab-43a1-a154-54e6321693dc"
/>
<img width="105" height="115" alt="image"
src="https://github.com/user-attachments/assets/5cbbf4d4-c8c0-4e55-a929-81373d5ccff5"
/>
<img width="78" height="70" alt="image"
src="https://github.com/user-attachments/assets/e7dd4cf4-4897-46f5-80ea-a79864587403"
/>
<img width="83" height="68" alt="image"
src="https://github.com/user-attachments/assets/a11cd131-1805-452f-a044-a34926072bc0"
/>
<img width="70" height="97" alt="image"
src="https://github.com/user-attachments/assets/740d7cb5-a3c0-477f-ba54-c8c518dc099c"
/>
<img width="105" height="94" alt="image"
src="https://github.com/user-attachments/assets/e0885c83-558d-4729-9355-49ce37feeb88"
/>
<img width="60" height="135" alt="image"
src="https://github.com/user-attachments/assets/8c1bd173-5483-4a9b-9e00-14a01d7cdb6a"
/>
<img width="96" height="65" alt="image"
src="https://github.com/user-attachments/assets/9665d0c7-f6e0-4269-92ef-3f1ff23dafa6"
/>
<img width="68" height="61" alt="image"
src="https://github.com/user-attachments/assets/8beaf270-8022-40ae-a9a2-fafc11e06e6c"
/>
<img width="82" height="143" alt="image"
src="https://github.com/user-attachments/assets/c1dc78d6-804d-4361-9e1d-53aa4bd73b67"
/>
<img width="104" height="127" alt="image"
src="https://github.com/user-attachments/assets/f7624e4c-d78d-4ba5-82cd-7e0f373ed488"
/>

Co-authored-by: Hidde Wieringa <hidde@hiddewieringa.nl>
using #657, we can now properly render multiple stop positions on the
same node.

and we can implement light-rail signals now, which was discussed in
#595 (comment)
(the german system seems to very similar)

stop positions:

<img width="99" height="98" alt="image"
src="https://github.com/user-attachments/assets/692ff89f-3ba5-4d28-b979-3b622c1971f8"
/> <img width="110" height="119" alt="image"
src="https://github.com/user-attachments/assets/b899171c-0505-4017-a385-c3f5b9d55b93"
/>


light-rail signals:

<img width="125" height="127" alt="image"
src="https://github.com/user-attachments/assets/90cd353a-d975-46b4-930b-b9ef7fd3f85f"
/>

<img width="148" height="286" alt="image"
src="https://github.com/user-attachments/assets/b27d9aeb-5951-432f-8d26-cf5ebd0d8671"
/>

---------

Co-authored-by: Hidde Wieringa <hidde@hiddewieringa.nl>
Fixes #697

Make the map localization configurable. Followup on #680 and #691.

No page reload needed. Even when changing browser language. The map will
automatically reload.

## Settings

### Custom language

<img width="838" height="258" alt="image"
src="https://github.com/user-attachments/assets/7381074b-d9af-4b09-9e25-a18fa2b01634"
/>
<img width="331" height="221" alt="image"
src="https://github.com/user-attachments/assets/8da421fc-ca91-464a-a854-a63624a8c425"
/>

### Automatic

<img width="838" height="258" alt="image"
src="https://github.com/user-attachments/assets/354fc8f8-0518-4feb-b2f7-1734498d45b6"
/>
<img width="344" height="278" alt="image"
src="https://github.com/user-attachments/assets/747c414d-c2b7-41c5-b5e6-dfdf376c56f7"
/>


### Disabled

<img width="826" height="182" alt="image"
src="https://github.com/user-attachments/assets/b6487928-0f81-4128-9b0f-09d50c8b1631"
/>
<img width="304" height="254" alt="image"
src="https://github.com/user-attachments/assets/2cc07497-7b1a-460d-9bf0-5aba2bf662e1"
/>


### Browser language

When the user changes browser language, the map is automatically
reloaded with the chosen language (if the automatic localization is
used).

<img width="294" height="119" alt="image"
src="https://github.com/user-attachments/assets/f5b5dcc0-9bbe-46ea-bb5b-968f0a2be4b1"
/>
Adds the additional gauges W9Plus, W10A, and combinations involving
thereof, as well as some extra combinations.

See comments & commits as to order.

---------

Co-authored-by: Hidde Wieringa <hidde@hiddewieringa.nl>
Co-authored-by: Hidde Wieringa <hidde@hiddewieringa.nl>
For #597 

Goal: add a fixed set of operators with a predefined list of metadata,
stored in
https://github.com/hiddewie/OpenRailwayMap-vector/blob/known-operators/features/operator.yaml.

Not every operator (thousands in the world) needs to be defined. Those
which are not defined will still get the same automatic generated color
like before.

Additionally, multiple operators on stations, and multiple networks on
stations are supported.

---------

Co-authored-by: wolfy1339 <4595477+wolfy1339@users.noreply.github.com>
Part of #661 

This pull request adds rendering of traction substations to the
electrification layer.
 
The name, reference, voltage, operator and location, as well as all the
default fields like images, notes and description are imported.

Eurotunnel
(http://localhost:8000/#view=14.33/51.09825/1.14809&style=electrification):
<img width="1324" height="557" alt="image"
src="https://github.com/user-attachments/assets/2e4e3032-e205-4d93-af18-2cdea2d84941"
/>
(hover)
<img width="1324" height="557" alt="image"
src="https://github.com/user-attachments/assets/ca797e2f-335a-4ae6-a1e2-5fbe1927437a"
/>
(dark mode)
<img width="1388" height="568" alt="image"
src="https://github.com/user-attachments/assets/92f9f1fb-abf5-4294-bf2f-8b8788bb98d8"
/>

Legend
<img width="533" height="614" alt="image"
src="https://github.com/user-attachments/assets/f0aa2e4c-0d21-4396-8fec-2af0a2c82132"
/>
Fixes #696.

Changes:
- Do not use convex hull a to create the "bubble" for railway yards,
like for multiple stations.
- Only show the outline (thicker, more transparent) for yards.
- Show a brown outline instead of orange.

Around New York (http://localhost:8000/#view=15.08/40.74294/-74.10442):



Default
<img width="1804" height="475" alt="image"
src="https://github.com/user-attachments/assets/2e7e1ccc-179b-4047-ace7-186d49684ddb"
/>


Hover
<img width="1804" height="475" alt="image"
src="https://github.com/user-attachments/assets/31a9e907-df4a-4e4d-b060-5dce7bbf2b9d"
/>

Popup
<img width="1804" height="475" alt="image"
src="https://github.com/user-attachments/assets/6bc89701-99d0-419b-9c22-6c99f4d5afa9"
/>

Zoomed out
<img width="1804" height="475" alt="image"
src="https://github.com/user-attachments/assets/18266b50-df1f-459c-a739-9b58751620d3"
/>



Smaller yard (http://localhost:8000/#view=17.41/40.708196/-73.93378):
<img width="1208" height="453" alt="image"
src="https://github.com/user-attachments/assets/4ed9b9b3-55e4-4f33-b227-5f8f2cee1348"
/>

Yard without area
(http://localhost:8000/#view=17.66/40.724383/-73.9164):
<img width="748" height="537" alt="image"
src="https://github.com/user-attachments/assets/6132cf31-3b3d-4aaf-be76-2fc0c36db8b9"
/>
Fixes #396

Changes:
- Fix healthcheck that verifies if the initial Postgres server restart
has completed.
- Modify Docker Compose to remove all unneeded configuration and
services.
- Update setup documentation with simplified command.
- Move tests to Docker Compose configuration.
- CI uses Docker Buildx Bake to read/write Github Actions cache for
Docker images. The `master` branch will read and write the caches, and
the branches will only read the caches.
- CI uses a single test run to start, import and run all tests, instead
of spreading over multiple jobs. This saves much time storing and
restoring data between jobs.
- Update deployment to add images explicitly in the deployment, and
ignore service dependencies.

CI time reduced from ~11 minutes to ~4 minutes.
hiddewie and others added 30 commits June 11, 2026 22:45
Followup on #993

Closes #988

<img width="807" height="483" alt="image"
src="https://github.com/user-attachments/assets/c59f94da-139e-4809-85ce-8352ca2fde41"
/>

Currently the individual voltages and frequencies are shown.

When two voltages and two frequencies are tagged, the conversion is
determined instead, formatted.

At this moment the `frequency_conversion` is not used at this point. See
https://wiki.openstreetmap.org/wiki/Proposal:Traction_substations_extension.
This is a draft of a pull request in collaboration with user
@The3dVehicleguy.

It will add more icons and more detailed rendering of US signals.

This PR is complete. Detailed description of this PR is here:
#952 (comment)

---------

Co-authored-by: Thomas Ray <the3dvehicleguy@gmail.com>
Co-authored-by: Hidde Wieringa <hidde@hiddewieringa.nl>
…998)

Noticed in
#976 (comment).

At the moment the colors of railway lines under construction or are
proposed are not rendered correctly and become gray.

The composed Maplibre expressions were not handled correctly in the
style.

Before and after
(https://openrailwaymap.app/#view=11.03/-28.6023/150.4&style=signals):
<img width="1843" height="947" alt="image"
src="https://github.com/user-attachments/assets/6914ff36-5fa2-4902-a3eb-e3d9f2713c99"
/>
Fixes #967 

If the user has enabled location tracking, the UI will request the
browser to disable sleep.

See https://developer.chrome.com/docs/capabilities/web-apis/wake-lock
See
https://stackoverflow.com/questions/6106747/can-i-prevent-phone-from-sleep-on-a-webpage
See https://w3c.github.io/screen-wake-lock/
See
https://maplibre.org/maplibre-gl-js/docs/API/classes/GeolocateControl/


Semantics:
- By default does not do anything
- When the user requests location tracking, the browser wake lock is
enabled
- When the user stops location tracking, the browser wake lock is
disabled
- When the user is using location tracking, but moves the map, the wake
lock is disabled until the user re-enables full location tracking
This PR should fix the recent failures in nightly updates.

Recent update to what details are shown for traction substations
improperly uses `%d` in `string.format`.\
`%d` accepts only integers, for real numbers `%f` should be used
instead. That's fixed in this PR.

Do note, that this PR changes the intended behaviour see changes in
tests.

---------

Co-authored-by: Hidde Wieringa <hidde@hiddewieringa.nl>
In GB, shunt and distant signals are displayed below main signals.
(See:

[https://en.wikipedia.org/wiki/UK_railway_signalling#Position_light_signals](https://en.wikipedia.org/wiki/UK_railway_signalling#Position_light_signals)
and

[https://en.wikipedia.org/wiki/UK_railway_signalling#Semaphore_signals](https://en.wikipedia.org/wiki/UK_railway_signalling#Semaphore_signals))

I've moved the shunt and distant signals before main signals in
signals_railway_signals.yaml so that they are displayed below the main
signal.

---------

Co-authored-by: Hidde Wieringa <hidde@hiddewieringa.nl>
**PR number 1000 🎉**

Fixes #979

Enabled by maplibre/maplibre-gl-js#1235 and
maplibre/maplibre-gl-js#5812. Supported since
Maplibre GL JS version 5.8.0.

See https://maplibre.org/maplibre-style-spec/layers/#line-dasharray

We can combine duplicated map layers that use different dashes. The
`line-dasharray` supports a data-driven style.

We still have to duplicate dashed lines and non-dashed lines, because
`line-cap` is not data-driven. For lines with a dasharray, the line cap
should be `butt` while for non-dashed lines the line cap should be
`round`.

Deduplicating railway lines with dashes will cause the map style to
contain less layers, causing the map to load quicker. Less layers will
also cause the map to show tiles quicker, because less layers have to be
evaluated for each feature on the map.

Layer differences:
- infrastructure: from 586 to 464
- speed: from 78 to 58
- train protection: from 144 to 166 (because of a correctness split of
dual/multi train protection system lines with/without dashes)
- electrification: from 134 to 74
- track: from 117 to 82
- operator: from 80 to 60
- routes: from 72 to 52

*View diff with whitespace disabled:
https://github.com/hiddewie/OpenRailwayMap-vector/pull/1000/changes?w=1*
See https://github.com/maplibre/martin/releases/tag/martin-v1.11.0,
https://github.com/maplibre/martin/releases/tag/martin-v1.10.0 and
https://github.com/maplibre/martin/releases/tag/martin-v1.9.0.

Features of interest:
- MVT - MLT pre-encoding, for #919 / #996
- Live reloading of PostgreSQL sources (views / functions), useful for
local development to avoid restarting Martin after re-importing data or
changing database views
Fixes #999

Tested on https://www.openstreetmap.org/relation/2100854 at
http://localhost:8000/#view=10.7/55.746/37.9149
<img width="635" height="679" alt="image"
src="https://github.com/user-attachments/assets/0ec03eee-d627-4c74-bbb2-39177377a188"
/>
<img width="674" height="346" alt="image"
src="https://github.com/user-attachments/assets/704e90f7-0d32-4326-942d-bf5950eddc55"
/>
<img width="1214" height="754" alt="image"
src="https://github.com/user-attachments/assets/40dab5ff-8c8d-4c6f-b270-c9889e916365"
/>
Having edited a lot of railways in Serbia I realized that they mostly
have `operator=Железнице Србије` that is wrong since 2015 when the
company was split according to the EU Fourth Railway Package. The
company managing the infrastructure is called [Инфраструктура Железнице
Србије](https://en.wikipedia.org/wiki/Serbian_Railways_Infrastructure).
I'm reluctant to batch rename until this is rendered properly here.

Apart from that Željeznice Republike Srpske operate in Republika Srpska
that is a constituent entity of Bosnia and Herzegovina, not Serbia, so I
moved the railway operator accordingly. Also fix a small mistake in some
Russian operator name.
The OSM data has been updated
Part of #1020.

Tiles loaded through automated processes like
https://github.com/SuperManifolds/rail_graph, and not through a browser,
do not send a `Referer` header by default. As the OpenRailwayMap tiles
are only meant to be used in a web interface, and not for downloading in
bulk by third parties, all requests without a referrer will be blocked.

Fow now, requests from origins other than https://openrailwaymap.app are
allowed, to support embedding the tiles in custom map styles on other
sites. For now the loading of tiles of those other sites, such as
https://preview.traintracker24.com/, https://www.railee.com/,
https://streckennetz.at/, https://app.hourrail.voyage/,
https://arnaud-upmre.github.io/, https://oe.fc-aa-test.mclarencloud.io/
and https://semilost.de/, is still allowed. In case of abuse, these
sites can still be blocked in the future.

Note that this is not a perfect security feature, because automated
processes can always set the `Referer` header manually. Let's hope the
authors are decent enough to not circumvent this block.

The usage policy will be documented in
https://github.com/hiddewie/OpenRailwayMap-vector/blob/master/USAGE.md.
## Summary
Added operator definitions for major Australian freight operators and
the Brisbane airport rail line to the operators configuration file.

## Key Changes
- **Freight operators**: Added 6 major Australian freight and rail
operators:
  - BHP (iron ore operations)
  - Rio Tinto (iron ore operations)
  - Hancock Prospecting (Roy Hill operations)
  - Fortescue Metals Group
  - Aurizon (freight and regional rail)
  - Bowen Rail Company

- **Airport rail**: Added Airtrain (Brisbane airport line) operator
definition

Co-authored-by: Hidde Wieringa <hidde@hiddewieringa.nl>
…1024)

Fixes #1014

This will ensure the values 1, 2 and 3 are classified as having the
Octys train protection system on the line, in use in France.

Tested on http://localhost:8000/#view=15/48.87427/2.34478&style=signals
<img width="966" height="699" alt="image"
src="https://github.com/user-attachments/assets/ad16543c-f139-456a-a02b-73b6a49284ec"
/>
~Not ready for merging, because these are pre-releases in
https://github.com/maplibre/maplibre-gl-js/releases~

~For compatibillity testing.~

~Maplibre GL JS migrated to Javascript modules. The UI code is also
migrated, which allows `import ...` statements. See
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules
for Javascript modules and mapping imports with `importmap`s.~

~To support efficient Javascript loading, HTML preload tags are added
for Javascript and JSON resources that are guaranteed to be loaded at a
later time, see
https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/preload,
https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/modulepreload
and https://web.dev/articles/modulepreload.~

See
https://github.com/maplibre/maplibre-gl-js/blob/v6.0.0/docs/guides/v5-to-v6-migration-guide.md

See https://github.com/maplibre/maplibre-gl-js/releases#release-v6.0.0
and https://github.com/maplibre/maplibre-gl-js/releases#release-v6.1.0

Maplibre GL JS 6 has been released. The distribution is a Javascript
module. The generation of missing images for the map (heavily used for
composed signal symbols) is supported better with an asynchronous
function.

The full migration to Javascript module for the other sources (UI and
HTML with embedded Javascript) will come in a separate pull request.
…es key support (#1021)

## Changed country code of operators
I corrected some country codes:
- LP -> JP (Japan)
- NK -> KP (Korea, People's Republic of)

## Added railway references in South Korea
- `ref:ARS`: ARS Reference number of KORAIL
- `ref:XROIS`: Reference number in Railway Operation Information System
(KORAIL)

## Added rubber_tires key support
Rails for rubber-tyred trains will be shown on information.
[Documentation](https://wiki.openstreetmap.org/wiki/Key:rubber_tires)

---------

Co-authored-by: Hidde Wieringa <hidde@hiddewieringa.nl>
As documented on
https://wiki.openstreetmap.org/wiki/DE:OpenRailwayMap/Tagging_Trams_in_Germany,
specifically those added with
https://wiki.openstreetmap.org/w/index.php?title=DE:OpenRailwayMap/Tagging_Trams_in_Germany&diff=0&oldid=3023848.

Some are city-specific. Not all are tagged in OpenStreetMap yet.

So 4 traversable
(http://localhost:8000/#view=19.65/51.114237/13.7838236&style=signals):
<img width="557" height="569" alt="image"
src="https://github.com/user-attachments/assets/37ebc341-da11-4718-9f27-402aff797cfe"
/>

Sh 13
(http://localhost:8000/#view=19.22/50.1124338/8.6879317&style=signals):
<img width="785" height="627" alt="image"
src="https://github.com/user-attachments/assets/28ce02d8-fb99-480e-b30b-d0cf27478760"
/>

Sh 9 (http://localhost:8000/#view=19/50.158613/8.687614&style=signals):
<img width="785" height="627" alt="image"
src="https://github.com/user-attachments/assets/b76b1a11-09a4-455f-9b96-2b9301f550e8"
/>

So 5a Potsdam
(http://localhost:8000/#view=19/52.3853027/13.0728293&style=signals):
<img width="628" height="459" alt="image"
src="https://github.com/user-attachments/assets/077fc51c-5bfd-4c4c-b577-0c799b498350"
/>

Engstelle So 8
(http://localhost:8000/#view=15.74/52.56257/13.868694&style=signals):
<img width="673" height="585" alt="image"
src="https://github.com/user-attachments/assets/a289518a-56f4-4af8-b6b9-c966acb422a9"
/>
Fixes #1016

Currently, the vacancy detection (insulated rail joints and axle
counters) are part of the train protection signals configuration file.
However, they are not a signal but rather points of interest for the
train protection layer.

This pull request moves the vacancy detection to the POI configuration,
and adds the POI source for the train protection layer.

Axle counter
(http://localhost:8000/#view=16.94/49.161341/8.49146&style=signals):
<img width="764" height="497" alt="image"
src="https://github.com/user-attachments/assets/b26a856b-ec2d-4ad8-9538-61474d910dd6"
/>

Insulated rail joint
(http://localhost:8000/#view=17.42/49.160644/8.491232&style=signals):
<img width="764" height="497" alt="image"
src="https://github.com/user-attachments/assets/f0b6d7ae-9b1b-4746-9bbb-eaa514e9c21f"
/>

Unknown vacancy detection
(http://localhost:8000/#view=17.44/48.926618/14.444146&style=signals):
<img width="764" height="497" alt="image"
src="https://github.com/user-attachments/assets/daf94e10-7895-4d29-9cf9-59c298778c1f"
/>

Legend:
<img width="446" height="497" alt="image"
src="https://github.com/user-attachments/assets/195508d9-c621-41f3-9e85-3956cb326add"
/>
See
https://wiki.openstreetmap.org/wiki/DE:OpenRailwayMap/Tagging_in_Switzerland
and https://www.bav.admin.ch/de/fahrdienstvorschriften-fdv.

Repeated signal for Vorsignal System N. See for example
https://youtu.be/durN0XW6M1Q?si=VrH5uRLju1y2qNH4&t=713
(http://localhost:8000/#view=18.15/46.719696/6.959488&style=signals):
<img width="699" height="660" alt="image"
src="https://github.com/user-attachments/assets/2b5ab5b5-c401-4bf9-82e6-0eead1999c0b"
/>

Vorsignal under hauptsignal, system L
(http://localhost:8000/#view=20/47.545893/7.6065415/-126/60&style=signals):
<img width="721" height="344" alt="image"
src="https://github.com/user-attachments/assets/6eac2a27-c11f-4741-bc04-a3a248aaf15e"
/>

Halt signal with train lengths
(http://localhost:8000/#view=16.7/47.547311/7.58767&style=signals):
<img width="960" height="524" alt="image"
src="https://github.com/user-attachments/assets/084154ce-8fb3-4e38-af05-405baf6e0921"
/>

Departure signals
(http://localhost:8000/#view=18.21/46.621554/8.572154&style=signals):
<img width="960" height="524" alt="image"
src="https://github.com/user-attachments/assets/ec4fa27b-8e83-4dab-852b-e4a0eb407bfa"
/>
…ckward (#1018)

Fixes #1017

## Summary

Fixes a bug where a way tagged with both a plain `maxspeed` and a
directional `maxspeed:forward`/`maxspeed:backward` rendered with no
speed color (gray) and no speed label, even though speed data was
present.

## Root cause

In `dominant_speed_label()` (`import/openrailwaymap.lua`), the `elseif
speed then return nil, nil end` branch treated any combination of a
plain `maxspeed` tag with a directional
`maxspeed:forward`/`maxspeed:backward` tag as ambiguous and discarded
all speed data. This produced `NULL` in the
`railway_line.maxspeed`/`speed_label` columns, which
`proxy/js/styles.mjs`'s `speedColor` expression renders as a plain gray
line:

```js
const speedColor = ['case',
  ['==', ['get', 'maxspeed'], null], 'gray',
  turboColorMap(['get', 'maxspeed'], 10, 380, 0.8),
]
```

## Fix

- Removed the branch that dropped all speed data for mixed tagging.
- Introduced `effective_forward_speed`/`effective_backward_speed`, which
fall back to the plain `maxspeed` value when a directional tag isn't
set, so the dominant speed and label are always computed when any speed
information is available.
- Consolidated the now-identical final `elseif`/`else` branches into one
`else`.

Example: `maxspeed=100`, `maxspeed:forward=120` (no preferred direction)
now imports as dominant speed `120` with label `"120 / 100"`, instead of
`NULL`/no label.

No DB schema, SQL view, or `styles.mjs` changes are needed — the bug was
purely in this Lua value computation.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Hidde Wieringa <hidde@hiddewieringa.nl>
Corrects GB banner repeaters - the central bar is black, not white.

Adds support to distinguish between banner repeaters with a green aspect
and those without. The default is white repeater (which I think is
overall more common).

Adds support for semaphore banner repeaters - light is the norm,
semaphore is *very* rare, so only semaphore should be differentiated.
The icon is the same as the white light one.

Relies on default if there is no `GB-NR:banner_green` to display just
the top-level aspect of `GB-NR:banner_off`.

I think it also works to the default if there is no such tag at all, but
I'm not sure on that.

There are compatible examples of tagging where the banner aspects have
been added around GB.
Added the current name of the national rail infrastructure operator in
North Macedonia. The current names are before the company split and
renamed.
)

Startup logged
```
api-1  |
api-1  |  ⚡ Starting FastAPI in production mode
api-1  | /app/api.py:12: StarletteDeprecationWarning: 'HTTP_422_UNPROCESSABLE_ENTITY' is deprecated. Use 'HTTP_422_UNPROCESSABLE_CONTENT' instead.
api-1  |   from openrailwaymap_api.facility_api import FacilityAPI
api-1  |
api-1  |  🐍 Using import string: api:app
api-1  |
api-1  |  🌐 Server started at http://0.0.0.0:5000
api-1  |     Documentation at http://0.0.0.0:5000/docs
```

See
https://www.rfc-editor.org/info/rfc9110/#name-422-unprocessable-content
Using the tiles from https://github.com/mapterhorn/mapterhorn increases
the hillshading resolution by quite a bit:
|Mapzen|Mapterhorn|
|-|-|
|<img
src="https://github.com/user-attachments/assets/0b9bdb20-281e-4aec-8ccc-b044953cc511"
/>|<img
src="https://github.com/user-attachments/assets/8c2a1687-2664-4899-b44d-d20020258d4d"
/>|

---------

Co-authored-by: Hidde Wieringa <hidde@hiddewieringa.nl>
For #970

Work in progress. 

Migrate the different map styles into a single Maplibre style. Use a
global state property `style` to switch the layers between the different
styles. Followup pull requests can merge the layers together which are
actually shared between styles.

No user visible changes.

*View diff with whitespace disabled!:
https://github.com/hiddewie/OpenRailwayMap-vector/pull/1036/changes?w=1*
See
https://wiki.openstreetmap.org/wiki/OpenRailwayMap/Signs_in_Italy#Announcement_boards_(Tavole_di_Orientamento)

Combines normal and distance announcement board types for simplicity.
Can be split later if needed.
Swiss signals 712/713 are "Einschaltsignale" so they should be
classified as `power_on` instead of `power_off`. This is documented
here:
https://wiki.openstreetmap.org/wiki/DE:OpenRailwayMap/Tagging_in_Switzerland#712,_713_Einschaltsignal
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.