Skip to content

Instantly share code, notes, and snippets.

@vielhuber
Last active January 13, 2025 13:34
Show Gist options
  • Save vielhuber/d6d023c0bcfb3d322272f2733e76315b to your computer and use it in GitHub Desktop.
Save vielhuber/d6d023c0bcfb3d322272f2733e76315b to your computer and use it in GitHub Desktop.
openstreetmap osm generate and output tiles tilemaker maplibre #server #html #js

links

installation

## create main folder
mkdir openstreetmap
cd openstreetmap

## osmium
mkdir osmium
cd osmium
wget https://github.com/osmcode/osmium-tool/archive/refs/tags/v1.16.0.tar.gz
tar -xzf v1.16.0.tar.gz
cd osmium-tool-1.16.0
apt-get install libosmium2-dev libprotozero-dev nlohmann-json3-dev libboost-program-options-dev libbz2-dev zlib1g-dev liblz4-dev libexpat1-dev cmake pandoc
mkdir build
cd build
cmake ..
make
make install
cd ..
cd ..
rm -rf ./osmium/
exec env -i HOME=$HOME bash -l
osmium --version

## mbutil
git clone https://github.com/mapbox/mbutil.git
cd mbutil
python setup.py install
cd ..
rm -rf ./mbutil/

## tilemaker
apt install build-essential libboost-dev libboost-filesystem-dev libboost-program-options-dev libboost-system-dev lua5.1 liblua5.1-0-dev libshp-dev libsqlite3-dev rapidjson-dev
git clone https://github.com/systemed/tilemaker.git
cd tilemaker
make
make install
cd ..
rm -rf ./tilemaker
exec env -i HOME=$HOME bash -l
tilemaker --help

usage

# settings
URL="https://download.geofabrik.de/europe/germany-latest.osm.pbf"
## bavaria (https://tools.geofabrik.de/calc/)
LAT_MIN=47.27 # bottom left breitengrad
LON_MIN=8.97 # bottom left längengrad
LAT_MAX=50.57 # top right breitengrad
LON_MAX=13.84 # top right längengrad

# download osm.pbf
wget -nc -O ./raw.osm.pbf "$URL"

# set bounding box
osmium extract --strategy=complete_ways --overwrite --bbox="$LON_MIN","$LAT_MIN","$LON_MAX","$LAT_MAX" --set-bounds ./raw.osm.pbf --output ./input.osm.pbf

# prepare tilemaker config + files
git clone https://github.com/systemed/tilemaker.git
sed -i -E 's|"compress": "(.+)"|"compress": "none"|' ./tilemaker/resources/config-openmaptiles.json ## modify config

# osm.pbf => pbf
rm -rf ./tiles/
tilemaker --input ./input.osm.pbf --output ./tiles --process ./tilemaker/resources/process-openmaptiles.lua --config ./tilemaker/resources/config-openmaptiles.json

# alternative: osm.pbf => mbtiles => pbf
#tilemaker ./input.osm.pbf --output ./tiles.mbtiles --process ./tilemaker/resources/process-openmaptiles.lua --config ./tilemaker/resources/config-openmaptiles.json
#mb-util ./tiles.mbtiles ./tiles --image_format=pbf

# copy boilerplate
wget -O ./index.html https://gist.githubusercontent.com/vielhuber/d6d023c0bcfb3d322272f2733e76315b/raw/index.html
wget -O ./fetch.php https://gist.githubusercontent.com/vielhuber/d6d023c0bcfb3d322272f2733e76315b/raw/fetch.php
sed -i -E 's/LAT_MIN: ([0-9]|\.)+/LAT_MIN: '"$LAT_MIN"'/' ./index.html
sed -i -E 's/LON_MIN: ([0-9]|\.)+/LON_MIN: '"$LON_MIN"'/' ./index.html
sed -i -E 's/LAT_MAX: ([0-9]|\.)+/LAT_MAX: '"$LAT_MAX"'/' ./index.html
sed -i -E 's/LON_MAX: ([0-9]|\.)+/LON_MAX: '"$LON_MAX"'/' ./index.html

# remove work files
rm -rf ./tilemaker/
rm -f ./input.osm.pbf
rm -f ./raw.osm.pbf
<?php
$force = true;
$force = false;
$file = 'style.json';
if (isset($_GET['file'])) {
if (!in_array($_GET['file'], ['metadata.json'])) {
die();
}
$file = $_GET['file'];
}
header('Content-Type: application/json; charset=utf-8');
$url = 'http' . (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 's' : '') . '://' . $_SERVER['HTTP_HOST'];
if ($file === 'style.json') {
if ($force === true || !file_exists('./tiles/style.json')) {
// rm -rf ./tiles/fonts/ ./tiles/style.json
@unlink('./tiles/style.json');
removeDir('./tiles/fonts/');
// download
file_put_contents(
'./tiles/tilemaker-master.zip',
file_get_contents('https://github.com/systemed/tilemaker/archive/refs/heads/master.zip')
);
// unzip
$zip = new \ZipArchive();
$res = $zip->open('./tiles/tilemaker-master.zip');
$zip->extractTo('./tiles/');
$zip->close();
// cp -R ./tiles/tilemaker-master/server/static/fonts ./tiles/
$cpSource = './tiles/tilemaker-master/server/static/fonts';
$cpDest = './tiles/fonts';
mkdir($cpDest);
foreach (
$iterator = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($cpSource, \RecursiveDirectoryIterator::SKIP_DOTS),
\RecursiveIteratorIterator::SELF_FIRST
)
as $item
) {
if ($item->isDir()) {
mkdir($cpDest . DIRECTORY_SEPARATOR . $iterator->getSubPathname());
} else {
copy($item, $cpDest . DIRECTORY_SEPARATOR . $iterator->getSubPathname());
}
}
// default style
if (1 === 1) {
// cp -R ./tilemaker/server/static/style.json ./tiles/
copy('./tiles/tilemaker-master/server/static/style.json', './tiles/style.json');
/*
wget -O ./tiles/sprite.json https://openmaptiles.github.io/osm-bright-gl-style/sprite.json
wget -O ./tiles/sprite.png https://openmaptiles.github.io/osm-bright-gl-style/sprite.png
*/
file_put_contents(
'./tiles/sprite.json',
file_get_contents('https://openmaptiles.github.io/osm-bright-gl-style/sprite.json')
);
file_put_contents(
'./tiles/sprite.png',
file_get_contents('https://openmaptiles.github.io/osm-bright-gl-style/sprite.png')
);
file_put_contents(
'./tiles/[email protected]',
file_get_contents('https://openmaptiles.github.io/osm-bright-gl-style/[email protected]')
);
file_put_contents(
'./tiles/[email protected]',
file_get_contents('https://openmaptiles.github.io/osm-bright-gl-style/[email protected]')
);
}
// custom style from https://github.com/teamapps-org/maplibre-gl-styles/
if (1 === 1) {
file_put_contents(
'./tiles/style.json',
file_get_contents(
'https://raw.githubusercontent.com/teamapps-org/maplibre-gl-styles/refs/heads/main/positron/style-cdn.json'
)
);
file_put_contents(
'./tiles/sprite.json',
file_get_contents(
'https://raw.githubusercontent.com/teamapps-org/maplibre-gl-styles/refs/heads/main/positron/sprite.json'
)
);
file_put_contents(
'./tiles/sprite.png',
file_get_contents(
'https://raw.githubusercontent.com/teamapps-org/maplibre-gl-styles/refs/heads/main/positron/sprite.png'
)
);
file_put_contents(
'./tiles/[email protected]',
file_get_contents(
'https://raw.githubusercontent.com/teamapps-org/maplibre-gl-styles/refs/heads/main/positron/[email protected]'
)
);
file_put_contents(
'./tiles/[email protected]',
file_get_contents(
'https://raw.githubusercontent.com/teamapps-org/maplibre-gl-styles/refs/heads/main/positron/[email protected]'
)
);
// custom fonts (https://maplibre.org/font-maker/)
removeDir('./tiles/fonts/');
file_put_contents(
'./tiles/fonts.zip',
file_get_contents('https://github.com/openmaptiles/fonts/releases/download/v2.0/v2.0.zip')
);
$zip = new \ZipArchive();
$res = $zip->open('./tiles/fonts.zip');
$zip->extractTo('./tiles/fonts/');
$zip->close();
}
// rm -rf ./tiles/tilemaker-master/
// rm -f ./tiles/tilemaker-master.zip
if (1 === 1) {
removeDir('./tiles/tilemaker-master/');
@unlink('./tiles/tilemaker-master.zip');
@unlink('./tiles/fonts.zip');
}
}
$content = file_get_contents('./tiles/' . $file);
$content = preg_replace(
'/"url": "(.+)\.json(.*)"/',
'"url": "' . $url . '/fetch.php?file=metadata.json"',
$content
);
$content = preg_replace(
'/"glyphs": "(.+)\/fonts\/\{fontstack\}\/\{range\}\.pbf(.*)"/',
'"glyphs": "' . $url . '/tiles/fonts/{fontstack}/{range}.pbf"',
$content
);
// concatenated fonts are not supported, since we don't have a tileserver
$content = preg_replace('/"text-font": \[(?:\n|.)*?"(.+)"(?:\n|.)+?\],/', '"text-font": ["$1"],', $content);
$content = preg_replace('/"sprite": "(.+)\/sprite"/', '"sprite": "' . $url . '/tiles/sprite"', $content);
}
if ($file === 'metadata.json') {
$content = file_get_contents('./tiles/' . $file);
$content = preg_replace(
'/"tiles":\["(.+)\/{z}\/{x}\/{y}.pbf"\]/',
'"tiles":["' . $url . '/tiles/{z}/{x}/{y}.pbf"]',
$content
);
}
echo $content;
die();
function removeDir($dir)
{
if (!is_dir($dir)) {
return;
}
$it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($files as $file) {
if ($file->isDir()) {
rmdir($file->getPathname());
} else {
unlink($file->getPathname());
}
}
rmdir($dir);
}
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5, minimum-scale=1" />
<title>openstreetmap</title>
<script src="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js"></script>
<link rel="stylesheet" href="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css" />
<script>
document.addEventListener('DOMContentLoaded', () => {
const boundingBox = {
LAT_MIN: 47.27,
LON_MIN: 8.97,
LAT_MAX: 50.57,
LON_MAX: 13.84
};
const boundingBoxArr = [
[boundingBox.LON_MIN, boundingBox.LAT_MIN],
[boundingBox.LON_MAX, boundingBox.LAT_MAX]
];
const map = new maplibregl.Map({
container: 'map',
style: 'fetch.php',
attributionControl: false, // remove copyright
maxBounds: boundingBoxArr,
center: [11.66, 49.05],
zoom: 10 // this does not work properly with maxBounds, see https://github.com/mapbox/mapbox-gl-js/issues/6969
});
map.fitBounds(boundingBoxArr);
map.addControl(
new maplibregl.NavigationControl({
showZoom: true,
showCompass: false
})
);
const marker = new maplibregl.Marker()
.setLngLat([11.66, 49.05])
.setPopup(
new maplibregl.Popup({ offset: 25 }).setHTML(`
<h2>Hello world</h2>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor.</p>
`)
)
.addTo(map);
});
</script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
#map {
width: 100wh;
height: 100vh;
}
.maplibregl-marker {
cursor: pointer;
}
</style>
</head>
<body>
<div id="map"></div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment