A personal project:

to create a global web-based topographic-scale 3D terrain map
based on open-source software and public-domain data

 

I have released the code I wrote to create these demos as

An experimental software for web-based 3D terrain cartography

 

 

Why cartolina might not be for you

  • a fork of a ten year old software project
  • a large, complex system
  • a user interface designed by Unix sytem programmers
  • no user community to speak of (yet)
  • little original documentation

... but bear with me ...

... because it does have some compelling features:

  • interactive cartographic renditions of massive DEMs
  • a native lighting model
  • scale-dependent vertical exaggeration
  • bump-mapping
  • background haze and foreground shadows
  • sun glints based on land-cover classification
  • arbitrary frames of reference, including extra-terrestrial bodies

cartolina is a narrowly-focused fork of vts-geospatial, originally by Melown Technologies / Leica Geosystems (2015-2023).


That was a massive system, involving 10+ components, over 20 supporting libraries and 70 software repositories.

Before cartolina could be released, I had to

  • simplify the architecture
  • refactor key parts of the codebase
  • implement and integrate the new functionality

  • just two components
  • purely client-side map configuration

The two components

cartolina-tileserver: a C++ Unix server daemon, manages data sources.

cartolina-js: a JavaScript / TypeScript library, renders data as interactive maps.

Hello, cartolina!

      <div id="map"></div>
<script type="module">
  import { map as createMap } from 
    'https://cdn.tspl.re/libs/cartolina/dist/current/cartolina.min.esm.js';

  let map = createMap({
      container: 'map',
      style: '/style.json',
      position: ["obj", 13.302, 47.038, "fix", 2479, -169, -90, 0, 14763, 30],
  });
</script>
      

Hello, cartolina: style.json

      {
    "version": 2,
    "sources": {
        "topoearth-copernicus-dem-glo30": {
            "type": "cartolina-surface",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/surface/topoearth/copernicus-dem-glo30/"
        }
    },
    "terrain": {
        "sources": ["topoearth-copernicus-dem-glo30"]
    },
    "illumination": {
        "light": ["tracking", 315, 45]
    },
    "vertical-exaggeration" : {
        "heightRamp": [[0,4000], [1.5,1.3]],
        "viewExtentProgression": [12.3, 13e6, 1.38, 1, 13.5]
    }
}
      

Adding layers: lightened satellite imagery

      {
    "version": 2,
    "sources": {
        "topoearth-copernicus-dem-glo30": {
            "type": "cartolina-surface",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/surface/topoearth/copernicus-dem-glo30/"
        },
        "eoxit-s2c": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/eoxit-sentinel2-cloudless-2018/"
        }
    },
    "terrain": {
        "sources": ["topoearth-copernicus-dem-glo30"]
    },
    "illumination": {
        "light": ["tracking", 315, 45]
    },
    "vertical-exaggeration" : {
        "heightRamp": [[0,4000], [1.5,1.3]],
        "viewExtentProgression": [12.3, 13e6, 1.38, 1, 13.5]
    },
    "layers": [
        { "source": "eoxit-s2c", "whitewash": 0.3 }
    ]
}
      

More interesting: natural colors

      {
    "version": 2,
    "sources": {
        "topoearth-copernicus-dem-glo30": {
            "type": "cartolina-surface",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/surface/topoearth/copernicus-dem-glo30/"
        },
        "ne1v6plcw": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/natural-earth1-v6p-lcw/" 
        },          
        "esa-worldcover2021": {
            "type": "cartolina-tms",
             "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/esa-worldcover2021-cr4/"
        }
    },
    "terrain": {
            "sources": ["topoearth-copernicus-dem-glo30"]
    },
    "illumination": {
            "light": ["tracking", 315, 45]
    },
    "vertical-exaggeration" : {
        "heightRamp": [[0,4000], [1.5,1.3]],
        "viewExtentProgression": [12.3, 13e6, 1.38, 1, 13.5]
    },
    "layers": [
        { "source": "ne1v6plcw" },
        { "source": "esa-worldcover2021" }
    ]
}
      

Adding a bump-map layer

      {
    "version": 2,
    "sources": {
        "topoearth-copernicus-dem-glo30": {
            "type": "cartolina-surface",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/surface/topoearth/copernicus-dem-glo30/"
        },
        "ne1v6plcw": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/natural-earth1-v6p-lcw/" 
        },          
        "esa-worldcover2021": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/esa-worldcover2021-cr4/"
        },
        "eoxit-s2c-normalmap": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/eoxit-s2c-2020-normalmap/"
        }
    },
    "terrain": {
        "sources": ["topoearth-copernicus-dem-glo30"]
    },
    "illumination": {
        "light": ["tracking", 315, 45]
    },
    "vertical-exaggeration" : {
        "heightRamp": [[0,4000], [1.5,1.3]],
        "viewExtentProgression": [12.3, 13e6, 1.38, 1, 13.5]
    },
    "layers": [
        {
            "type": "bump-map",
            "source": "eoxit-s2c-normalmap",
            "alpha": 0.2
        },
        {
            "source": "ne1v6plcw",
            "blendMode": "overlay",
            "alpha": { "mode": "constant", "value": 1.0 }
        },
        {
            "type": "diffuse-map",
            "source": "esa-worldcover2021"
        },
        {
            "type": "constant",
            "source": [255,255,255],
            "blendMode": "overlay",
            "alpha": 0.15
        }
    ]
}
      

Adding sun glints (specular reflection layers)

      {
    "version": 2,
    "sources": {
        "topoearth-copernicus-dem-glo30": {
            "type": "cartolina-surface",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/surface/topoearth/copernicus-dem-glo30/"
        },
        "ne1v6plcw": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/natural-earth1-v6p-lcw/" 
        },          
        "esa-worldcover2021": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/esa-worldcover2021-cr4/"
        },
        "eoxit-s2c-normalmap": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/eoxit-s2c-2020-normalmap/"
        },
        "ne1lc-specularmap": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/ne1-lc-500m-specularmap/"
        },
        "esa-worldcover-specularmap": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/esa-worldcover-2021-specularmap/"
        }
    },
    "terrain": {
        "sources": ["topoearth-copernicus-dem-glo30"]
    },
    "illumination": {
        "light": ["tracking", 315, 45]
    },
    "vertical-exaggeration" : {
        "heightRamp": [[0,4000], [1.5,1.3]],
        "viewExtentProgression": [12.3, 13e6, 1.38, 1, 13.5]
    },
    "layers": [
        {
            "type": "bump-map",
            "source": "eoxit-s2c-normalmap",
            "alpha": 0.2,
            "necessity": "optional"
        },
        {
            "source": "ne1v6plcw",
            "blendMode": "overlay",
            "alpha": { "mode": "constant", "value": 1.0 }
        },
        {
            "type": "diffuse-map",
            "source": "esa-worldcover2021"
        },
        {
            "type": "constant",
            "source": [255,255,255],
            "blendMode": "overlay",
            "alpha": 0.15
        },
        {
            "type": "specular-map",
            "terrain": ["topoearth-copernicus-dem-glo30"],
            "source": "ne1lc-specularmap"
        },
        {
            "type": "specular-map",
            "source": "esa-worldcover-specularmap"
        }
    ]
}
      

Adding haze and shadows

      {
    "version": 2,
    "sources": {
        "topoearth-copernicus-dem-glo30": {
            "type": "cartolina-surface",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/surface/topoearth/copernicus-dem-glo30/"
        },
        "ne1v6plcw": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/natural-earth1-v6p-lcw/" 
        },          
        "esa-worldcover2021": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/esa-worldcover2021-cr4/"
        },
        "eoxit-s2c-normalmap": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/eoxit-s2c-2020-normalmap/"
        },
        "ne1lc-specularmap": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/ne1-lc-500m-specularmap/"
        },
        "esa-worldcover-specularmap": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/esa-worldcover-2021-specularmap/"
        }
    },
    "terrain": {
        "sources": ["topoearth-copernicus-dem-glo30"]
    },
    "illumination": {
        "light": ["tracking", 315, 45]
    },
    "vertical-exaggeration" : {
        "heightRamp": [[0,4000], [1.5,1.3]],
        "viewExtentProgression": [12.3, 13e6, 1.38, 1, 13.5]
    },
    "atmosphere": {
        "visibilityToEyeDistance": 3.0,
        "edgeDistanceToEyeDistance": 1.0,
        "maxVisibility": 1e6
    },
    "shadows": {},
    "layers": [
        {
            "type": "bump-map",
            "source": "eoxit-s2c-normalmap",
            "alpha": 0.2,
            "necessity": "optional"
        },
        {
            "source": "ne1v6plcw",
            "blendMode": "overlay",
            "alpha": { "mode": "constant", "value": 1.0 }
        },
        {
            "type": "diffuse-map",
            "source": "esa-worldcover2021"
        },
        {
            "type": "constant",
            "source": [255,255,255],
            "blendMode": "overlay",
            "alpha": 0.15
        },
        {
            "type": "specular-map",
            "terrain": ["topoearth-copernicus-dem-glo30"],
            "source": "ne1lc-specularmap",
            "necessity": "essential"
        },
        {
            "type": "specular-map",
            "source": "esa-worldcover-specularmap",
            "necessity": "essential"
        }
    ]
}
      

Lettering

cartolina  provides rudimentary support for lettering.

Point labels are solid with a well-defined visual hierarchy.

Line labels are weak.

Area labels have not been implemented.

Lettering with AST syntax

      {
    "version": 2,
    "sources": {
        "topoearth-copernicus-dem-glo30": {
            "type": "cartolina-surface",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/surface/topoearth/copernicus-dem-glo30/"
        },
        "ne1v6plcw": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/natural-earth1-v6p-lcw/" 
        },          
        "esa-worldcover2021": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/esa-worldcover2021-cr4/"
        },
        "eoxit-s2c-normalmap": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/eoxit-s2c-2020-normalmap/"
        },
        "ne1lc-specularmap": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/ne1-lc-500m-specularmap/"
        },
        "esa-worldcover-specularmap": {
            "type": "cartolina-tms",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/tms/topoearth/esa-worldcover-2021-specularmap/"
        },
        "osm-openfreemap": {
            "type": "cartolina-freelayer",
            "url": "https://cdn.tspl.re/mapproxy/melown2015/geodata/topoearth/osm-openfreemap/"
        }
    },
    "terrain": {
        "sources": ["topoearth-copernicus-dem-glo30"]
    },
    "illumination": {
        "light": ["tracking", 315, 45]
    },
    "vertical-exaggeration" : {
        "heightRamp": [[0,4000], [1.5,1.3]],
        "viewExtentProgression": [12.3, 13e6, 1.38, 1, 13.5]
    },
    "atmosphere": {
        "visibilityToEyeDistance": 3.0,
        "edgeDistanceToEyeDistance": 1.0,
        "maxVisibility": 1e6
    },
    "shadows": {},
    "constants": {
        "@osmid": {"if":[["has","$id_"],"$id_",""]},
        "@name": {"if":[["has","$name"],{"if":[["any",["!has","$name:en"],["==",{"has-latin":"$name"},true]],"{$name}","{$name}\n{$name:en}"]},""]},
        "@serif-font": ["noto-mix","noto-cjk"],
        "@italic-font": ["noto-italic","noto-mix","noto-cjk"]
    },
    "fonts": {
        "noto-italic": "https://cdn.tspl.re/fonts/noto-italic/1.0.0/noto-i.fnt",
        "noto-mix": "https://cdn.tspl.re/fonts/noto-extended/1.0.0/noto.fnt",
        "noto-cjk": "https://cdn.tspl.re/fonts/noto-cjk/1.0.0/noto.fnt",
        "noto-serif": "https://cdn.tspl.re/fonts/noto-serif/1.0.0/noto-serif.fnt",
        "#default": "https://cdn.tspl.re/libs/vtsjs/fonts/noto-extended/1.0.0/noto.fnt"
    },
    "layers": [
        {
            "type": "bump-map",
            "source": "eoxit-s2c-normalmap",
            "alpha": 0.2,
            "necessity": "optional"
        },
        { "source": "ne1v6plcw" },
        { "source": "esa-worldcover2021" },
        {
            "type": "constant",
            "source": [255,255,255],
            "blendMode": "overlay",
            "alpha": 0.15
        },
        {
            "type": "specular-map",
            "terrain": ["topoearth-copernicus-dem-glo30"],
            "source": "ne1lc-specularmap",
            "necessity": "essential"
        },
        {
            "type": "specular-map",
            "source": "esa-worldcover-specularmap",
            "necessity": "essential"
        },
        {
            "id": "places",
            "type": "labels",
            "source": "osm-openfreemap",
            "filter": ["all",["==","#group","place"],["in","$class","city","town","village","suburb","hamlet"]],
            "&population": {"str2num":{"if":[["has","$population"],"$population",1]}},
            "&mt-rank": {"str2num":{"if":[["has", "$rank"],"$rank",11]}},
            "&importance": {"linear2":["&mt-rank",[[3,90],[11,10]]]},
            "&rank": {"linear2":["&mt-rank",[[1,1],[11,6]]]},
            "importance-source": "&importance",
            "label": true,
            "label-source": "@name",
            "label-size": {"linear2":["&rank",[[0,25],[6,14]]]},
            "label-font": "@italic-font",
            "label-color": [0,0,0,192],
            "label-color2": [255,255,255,192],
            "label-outline": [0.5,0.7,2.2,2.2],
            "zbuffer-offset": [-0.35,0,0],
            "culling": 90,
            "&id": "{@osmid} {@name}",
            "hysteresis": [1500,1500,"&id",true]
        },
        {
            "id": "peaks",
            "type": "labels",
            "source": "osm-openfreemap",
            "filter": ["all",["==","#group","mountain_peak"],["in","$class","peak","volcano","saddle"],["has","$name"],["!=","$name",""]],
            "&prominence": {"add":[
                {"if":[["has","$ele"],{"mul":[0.0001,{"str2num":"$ele"}]},0]},
                {"if":[["has","$prominence"],{"mul":[0.3048,{"str2num":"$prominence"}]},0]}]},
            "&importance": {"logScale":["&prominence",8848.8848]},
            "&rank": {"discrete2":["&prominence",[[0,6],[30,5],[70,4],[150,3],[300,2],[700,1],[1500,0]]]},
            "importance-source": "&importance",
            "&feet": {"round":{"mul":[3.2808399,{"str2num":"$ele"}]}},
            "&elevation-name": {"if":[["==","#metric",true],"{{'round':{'str2num':'$ele'}}} m","{&feet} ft"]},
            "&prominence-name": "{{'round':'&prominence'}} m",
            "&peak-name": {"if":[["==","&rank",0],{"uppercase":"{@name}"},"{@name}"]},
            "&peak-name2": {"if":[["has","$ele"],"{&peak-name}\n{&elevation-name}","{@peak-name}"]},
            "label": true,
            "label-source": "&peak-name2",
            "label-size": {"linear2":["&rank",[[0,20],[6,14]]]},
            "label-font": "@serif-font",
            "label-color":  {"linear2":["&rank",[[1,[0,0,0,255]],[6,[0,0,0,160]]]]},
            "label-color2": {"linear2":["&rank",[[1,[255,255,255,96]],[6,[255,255,255,60]]]]},
            "label-outline": [0.2,0.65,2.2,10],
            "label-origin": "top-center",
            "label-offset": [0,30],
            "label-align": "left",
            "zbuffer-offset": [-0.45,0,0],
            "culling": 100,
            "&id": "{@osmid} {&peak-name}",
            "hysteresis": [1500,1500,"&id",true]
        },
        {
            "id": "country-boundaries",
            "type": "lines",
            "source": "osm-openfreemap",
            "filter": ["all",["==","#group","boundary"],["==","$admin_level","2"],["!=","$maritime","1"]],
            "line": true,
            "line-flat": false,
            "line-width": 4,
            "line-color": [143,117,82,128],
            "zbuffer-offset": [-0.01,0,0]
        },
        {
            "id": "state-boundaries",
            "type": "lines",
            "source": "osm-openfreemap",
            "filter": ["all",["==","#group","boundary"],["==","$admin_level","4"],["!=","$maritime","1"]],
            "line": true,
            "line-flat": false,
            "line-width": 2,
            "line-color": [143,117,82,128],
            "zbuffer-offset": [-0.01,0,0]
        }
    ]
}
      

https://cartolina.dev/

Your feedback is appreciated

E-mail: ondrej@tspl.re, Twitter/X: @ondrej1974