openfreemap
openfreemap copied to clipboard
Outdoor style possible?
I wanted to propose an outdoor style to openfreemap but I have the impression that it lacks data to succeed in doing something like.
https://www.maptiler.com/maps/outdoor/
After all, I'm just starting out, so maybe I'm looking in the wrong place, but the relationship between a hiking trail doesn't seem to be there for the drawing or the trail_visibility tag.
I'm having trouble I suppose because I know OSM and I can't manage to do the mapping properly.
I never made an outdoor map, but I believe there are two components required:
- elevation data, to create hillshading and contour lines
- hiking specific tags, etc.
Elevation data is tracker here: https://github.com/hyperknot/openfreemap/issues/19
For hiking specific tags, you'd need to use the inspector and see what is and what isn't visible. Can you check for the tags using the inspector? https://github.com/hyperknot/openfreemap/blob/main/docs/debugging_names.md
We are using maptiler outdoor map extensively at osmapp.org – there are 2 vector map sources in their outdoor style:
- standard
maptiler_planet, which is the same as OpenFreeMap planet - specific hiking source
outdoor, which holds the geometries of the marked trails and some additional hiking POIs –https://api.maptiler.com/tiles/outdoor/tiles.json?key=${apiKey}, seesource: 'outdoor'in the style here
Of course there are also 2 sources for digital elevation models (terrain-rgb + countours), but this is probably beyond the scope of OFR project. See here.
I wonder how much bigger would get the OFR planet, if we included the hiking tags. Maptiler decided to split it in two, but if I understand it correctly, they are basically duplicating all the geometries in both tiles, so just adding the hiking tags in OFR planet could be doable and awesome! 🙂
I'd love to include the hiking tags. I don't have capacity to dedicate for this effort, but if you can figure out how to include custom tags in planetiler, I'd be happy to include those in the official OFM planet source. I'd recommend opening an issue/discussion in planetiler GitHub.
Terrain related data will be included in the long term future: https://github.com/hyperknot/openfreemap/issues/19
I don't have the skills yet but I'm trying to understand how it works. If one day I can advance the technical subject I would be delighted.
There'll be ready-made styles when this is done, you won't need to have advanced technical understanding to add outdoor maps to a website/app at that point.
Quick question, do you host data in pmTiles or mbtiles? Because you can create your own profile for planetiler and add the information.
PMTiles is purpose built for serverless hosting on AWS or Cloudflare. You upload the huge file to a R2 bucket and deploy a worker in front of it. It is explained here: https://docs.protomaps.com/deploy/cloudflare
If the question is how is OpenFreeMap storing vector tiles, it's neither, it's using a Btrfs disk image.
I wanted to know if to make the tiles the command of this style was used: java -Xmx1g -jar planetiler.jar --download --area=monaco
Because if so, we can create a file that allows us to overload the existing profile to add tags.
OK, now I understand your question.
This is the command being run: https://github.com/hyperknot/openfreemap/blob/a3eeed00b5eedbbd40568bc61bc0d6589ba0c710/modules/tile_gen/tile_gen_lib/planetiler.py#L35-L49
And yes, I'm open to adding tourist map specific tags to the default profile, such that one day we can create maps like WayMarkedTrails:
By simplifying the documentation and using Java 22, we can create profiles.
So I tested it by inheriting from the base profile to get all the information we already use and adding an outdoor layer that could contain additional information.
I may have added a bit too much, but I was just testing. I wanted to get the surface area with the real OSM information plus the tags that I find interesting for hiking. I also looked at how to find the relationships.
Currently I am running this command
java -cp planetiler.jar CustomProfile.java
And in my data directory I have the custom.pmtiles file which is exported
For test: http://83.228.195.237:8888/data/rando/#15.54/45.232238/5.757771
import com.onthegomap.planetiler.Profile;
import com.onthegomap.planetiler.config.Arguments;
import com.onthegomap.planetiler.reader.SourceFeature;
import com.onthegomap.planetiler.FeatureCollector;
import com.onthegomap.planetiler.Planetiler;
import java.nio.file.Path;
import org.openmaptiles.OpenMapTilesProfile;
import org.openmaptiles.layers.Transportation;
import com.onthegomap.planetiler.util.Translations;
import com.onthegomap.planetiler.config.PlanetilerConfig;
import com.onthegomap.planetiler.stats.Stats;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import com.onthegomap.planetiler.reader.osm.OsmRelationInfo;
import com.onthegomap.planetiler.reader.osm.OsmElement;
import java.util.ArrayList;
public class CustomProfile extends OpenMapTilesProfile {
public CustomProfile(Planetiler runner) {
super(runner.translations(), runner.config(), runner.stats());
}
public CustomProfile(Translations translations, PlanetilerConfig config, Stats stats) {
super(translations,config,stats);
}
@Override
public void processFeature(SourceFeature sourceFeature, FeatureCollector features) {
super.processFeature(sourceFeature, features);
// Puis ajoute sac_scale à transportation
if (sourceFeature.canBeLine() && sourceFeature.hasTag("highway") &&
(
sourceFeature.hasTag("sac_scale") ||
sourceFeature.hasTag("surface") ||
sourceFeature.hasTag("trail_visibility") ||
sourceFeature.hasTag("tracktype")
)) {
var line = features.line("outdoor")
.inheritAttrFromSource("highway"); // récupère d'autres attributs utiles
if (sourceFeature.hasTag("tracktype")) {
line.setAttr("tracktype", sourceFeature.getTag("tracktype"));
}
if (sourceFeature.hasTag("sac_scale")) {
line.setAttr("sac_scale", sourceFeature.getTag("sac_scale"));
}
if (sourceFeature.hasTag("surface")) {
line.setAttr("surface", sourceFeature.getTag("surface"));
}
if (sourceFeature.hasTag("trail_visibility")) {
line.setAttr("trail_visibility", sourceFeature.getTag("trail_visibility"));
}
int i = 0;
for (var routeInfo : sourceFeature.relationInfo(RouteRelationInfo.class)) {
// (routeInfo.role() also has the "role" of this relation member if needed)
RouteRelationInfo relation = routeInfo.relation();
line.setAttr("relation_"+i+"_name", relation.name);
line.setAttr("relation_"+i+"_ref", relation.ref);
line.setAttr("relation_"+i+"_route", relation.route);
line.setAttr("relation_"+i+"_network", relation.network);
line.setAttr("relation_"+i+"_symbol", relation.symbol);
line.setAttr("relation_"+i+"_color", relation.color);
i++;
}
}
}
private record RouteRelationInfo(
// OSM ID of the relation (required):
@Override long id,
// Values for tags extracted from the OSM relation:
String name, String ref, String route, String network,String symbol,String color
) implements OsmRelationInfo {}
@Override
public List<OsmRelationInfo> preprocessOsmRelation(OsmElement.Relation relation) {
// If this is a "route" relation ...
if (relation.hasTag("type", "route")) {
// where route=bicycle or route=mtb ...
if (relation.hasTag("route","hiking")) {
// then store a RouteRelationInfo instance with tags we'll need later
return List.of(new RouteRelationInfo(
relation.id(),
relation.getString("name"),
relation.getString("ref"),
relation.getString("route"),
relation.getString("network", ""),
relation.getString("osmc:symbol", ""),
relation.getString("colour", "")
));
}
}
// for any other relation, return null to ignore
return null;
}
public static void main(String[] args) {
// arguments CLI avec download par défaut
var arguments = Arguments.fromArgs(args).withDefault("download", true);
// création de Planetiler
Planetiler runner = Planetiler.create(arguments);
// profil personnalisé
var profile = new CustomProfile(runner);
// exécution
runner.setProfile(profile)
.addOsmSource("osm", Path.of("data.osm.pbf"))
.overwriteOutput(Path.of("data", "custom.pmtiles"))
.run();
}
}
The idea is not that relationships like waymarked trail are also to display in a more relevant way a visible path from a path that is not, from a path that is part of a network. There is a debate currently on OSM on hiking trails because many renderings display the same thing for completely different paths.
At first I wanted to make a data source just to add information like maptiler but I realized that when we do that we don't guarantee the data and suddenly we can have a desynchronization and display 2 paths when it should only be one
Example with maptiler outdoor:
You mean Maptiler Outdoor is using an hiking overlay on top of their normal map layer?
Is there any "standard" or is the community converging on something which would be a good base for creating such schema?
I'm sure someone must have created OpenMapTiles based hiking maps. Is there any open source schema for it?
Yes, last year, Maptiler added an outdoor source to display hiking relation.
I haven't found anything along these lines. I have the impression that everyone renders for cars and then deviates a bit for outdoor use, but without going into detail.
I know of raster renderers in France that are good, but they can't currently be recreated in vector without information on bag_scale and trail_visibility.
If you can research what would make a good looking global hiking map and give a recommendation for a planetiler profile, I'm happy to include it. It'd be nice to render something which looks nice and is usable on France, Germany, Austria, etc. especially as they might use incompatible systems.
Do you want me to do the styling already?
Sure, if you are open to do it, then it'd be fantastic! I don't know if you follow the other thread, but Mapterhorn just released these amazing terrain/hillshade dataset, you can use that: https://github.com/hyperknot/openfreemap/issues/19#issuecomment-3174527733
https://mapterhorn.com/
I think the best resource on rendering hiking OSM tags is here: https://hiking.waymarkedtrails.org/#help-rendering
What do you think, we can start with a basic version of this and keep adding to the planetiler profile later on?
I have to look into how to do it, currently I can't do it, I have the impression that I have to have https for it to work
For you, the need is therefore to display the relations first, which can be complicated because a path can be in x relations and so which one should you draw? the international one if it exists otherwise the national one if it exists ....
I mean you can just link to the hosted https://mapterhorn.com/ pmtiles, can't you? If you need https locally, the simplest I found was to use cloudflare tunnel. It's a free ngrok alternative.
About the relations, I don't yet know as much as you, but I imagine the ultimate goal to be something like this, where E4 is international and the others are local, and they are all rendered nicely, next to each other. This might be crazily complicated/impossible in planetiler + MapLibre, so it's just a reference.
Thunderforest does this, which might be technically simpler:
MapToolkit does this:
Again, I don't yet know technically which of these might be simple and which are complicated.
It'd be nice to come up with a system for the:
- line of the route, like color, width, dashed, pointed, etc.
- tags to show on the route, like on the screenshots above
For the https, it's because the server I'm using is HTTP, and therefore Mapunik doesn't allow me to reference the tiles I've placed on it. I'll look into Cloudflare.
Sure, Cloudflare Tunnels will solve it, even if it's a local dev server. If it's a public server, just deploy Cloudflare in front of it (I mean set up an A record for that server + enable the "orange cloud" button).
Nice post about using Mapterhorn https://jonathanlurie.substack.com/p/basemapkit-lets-explore-the-mountains