Dynamic layers using

Mapserver,

Planetary spatial reference systems

and Python

Sebastian Walter, Freie Universitaet Berlin

Background: HRSC Mapserver

maps.planet.fu-berlin.de (Walter et al., 2018)

#### Requirements - *Dynamic* map interface with: - Global basemaps - Single images (*dynamic* layers) - Measurements - Scalebar - Custom projections - Performance (mainly rasters, tile cache) - Open source (sustainability)
#### Main components - [OpenLayers](https://openlayers.org/) - [MapServer](https://mapserver.org/products.html) - [MapServer Core](https://mapserver.org/documentation.html) - [MapScript](https://mapserver.org/mapscript/index.html) - [MapCache](https://mapserver.org/mapcache/index.html)
#### OpenLayers entry point - [Tutorials](https://openlayers.org/en/latest/doc/tutorials/) - Building an OpenLayers Application - Node - Parcel as bundler - Javascript modules

Openlayers parcel tutorial

Custom Mars layer

MapServer: mars.map

                  MAP
                  NAME "MEx/HRSC global imagery and terrain data."
                  MAXSIZE 40960
                  UNITS meters
                  EXTENT -10668837.5 -5213575.0 10668900.0 5215725.0
                  WEB
                    METADATA
                      wms_onlineresource  "https://maps.planet.fu-berlin.de/eqc-bin/wms?"
                      "wms_srs" "EPSG:4326"
                      wms_enable_request "*"
                    END
                  END #web
                  LAYER
                      NAME "MOLA-gray-hs"
                      TYPE RASTER
                      DATA MOLA-DTM-aeroid-hs-z3.tif
                      PROJECTION
                          proj=eqc
                          lat_ts=0
                          lat_0=0
                          lon_0=0
                          x_0=0
                          y_0=0
                          a=3396000
                          b=3396000
                          units=m
                      END
                  END #layer
                  END #map
                  

Custom Mars layer

OpenLayers: index.js

                              ...
                              import TileWMS from "ol/source/TileWMS";

                              const map = new Map({
                                target: 'map',
                                layers: [
                                  new TileLayer({
                                    source: new TileWMS({
                                      url: "https://maps.planet.fu-berlin.de/eqc-bin/wms?",
                                      params: { LAYERS: "MOLA-gray-hs"}
                                    })
                                  })
                                ],
                                view: new View({
                                  center: [0, 0],
                                  zoom: 0,
                                  projection: 'EPSG:4326'
                                })
                              });
                          

Scale line

Openlayers: index.js

    import {defaults as defaultControls, ScaleLine} from 'ol/control';

    const map = new Map({
      target: 'map',
      layers: [
        new TileLayer({
          source: new TileWMS({
            url: "https://maps.planet.fu-berlin.de/eqc-bin/wms?",
            params: { LAYERS: "MOLA-gray-hs", VERSION: "1.1.1"}
          })
        })
      ],
      controls: defaultControls().extend([
        new ScaleLine({
          units: 'metric'
        })
      ]),
      view: new View({
        center: [0, 0],
        zoom: 0,
        projection: 'EPSG:4326'
      })
    });
                      

Custom projection

MapServer: /usr/share/proj/epsg


                    # Mars equirectangular trunc
                    <49910> +proj=eqc +lat_ts=0 +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +a=3396000 +b=3396000 +units=m +no_defs <>
                    

MapServer: mars.map


                    MAP
                    NAME "MEx/HRSC global imagery and terrain data."
                    MAXSIZE 40960
                    UNITS meters
                    EXTENT -10668837.5 -5213575.0 10668900.0 5215725.0
                    WEB
                      METADATA
                        wms_onlineresource  "https://maps.planet.fu-berlin.de/eqc-bin/wms?"
                        "wms_srs" "EPSG:49910 EPSG:4326"
                        wms_enable_request "*"
                      END
                    END
                    LAYER
                        NAME "MOLA-gray-hs"
                        TYPE RASTER
                        DATA MOLA-DTM-aeroid-hs-z3.tif
                        PROJECTION
                            proj=eqc
                            lat_ts=0
                            lat_0=0
                            lon_0=0
                            x_0=0
                            y_0=0
                            a=3396000
                            b=3396000
                            units=m
                        END
                    END #layer
                    

Custom projection

Openlayers: index.js

    import 'ol/ol.css';
    import {Map, View} from 'ol';
    import TileLayer from 'ol/layer/Tile';
    import TileWMS from 'ol/source/TileWMS';
    import {defaults as defaultControls, ScaleLine} from 'ol/control';
    import {register} from 'ol/proj/proj4';
    import {Projection} from 'ol/proj';
    import proj4 from 'proj4';

    proj4.defs('EPSG:49910', '+proj=eqc +lat_ts=0 +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +a=3396000 +b=3396000 +units=m +no_defs ');
    register(proj4);

    var projection = new Projection({
      code: "EPSG:49910",
      global: true,
      extent: [-10668848.652, -5215881.563, 10668848.652, 5215881.563]
    });

    const map = new Map({
      target: 'map',
      layers: [
        new TileLayer({
          source: new TileWMS({
            url: "https://maps.planet.fu-berlin.de/eqc-bin/wms?",
            params: { LAYERS: "MOLA-gray-hs", VERSION: "1.1.1"}
          })
        })
      ],
      controls: defaultControls().extend([
        new ScaleLine({
          units: 'metric'
        })
      ]),
      view: new View({
        center: [0, 0],
        zoom: 0,
        projection: projection
      })
    });
                      

Mars projection

Openlayers: index.js

  ...
  import { Projection, getTransform, get } from "ol/proj";
  import { getDistance } from "ol/sphere";

  proj4.defs("EPSG:49900", "+proj=longlat +a=3396000 +b=3396000 +no_defs ");
  proj4.defs("EPSG:49910", "+proj=eqc +lat_ts=0 +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +a=3396000 +b=3396000 +units=m +no_defs ");
  register(proj4);

  var projection = new Projection({
    code: "EPSG:49910",
    global: true,
    extent: [-10668848.652, -5215881.563, 10668848.652, 5215881.563],
    getPointResolution: function(resolution, point) {
      var toEPSG49900 = getTransform(get("EPSG:49910"), get("EPSG:49900"));
      var vertices = [
        point[0] - resolution / 2, point[1],
        point[0] + resolution / 2, point[1]
      ];
      vertices = toEPSG49900(vertices, vertices, 2);
      return getDistance(vertices.slice(0, 2), vertices.slice(2, 4), 3396000);
    }
  });

  const map = new Map({
    target: "map",
    layers: [
      new TileLayer({
        source: new TileWMS({
          url: "https://maps.planet.fu-berlin.de/eqc-bin/wms?",
          params: { LAYERS: "MOLA-gray-hs" }
        })
      })
    ],
    controls: defaultControls().extend([
      new ScaleLine({
        units: "metric"
      })
    ]),
    view: new View({
      center: [0, 0],
      zoom: 0,
      projection: projection
    })
  });
                      

Single image

MapServer:

LAYER
  NAME "h3286_0000.ihs.04"
  TYPE RASTER
        DATA hrsc/mex4/eqc/ihs/h3286_0000.ihs.04.tif
  PROJECTION
    "init=epsg:49910"
  END
  METADATA
    "wms_enable_request" "*"
    "wms_abstract"  "HRSC single sequence h3286_0000.ihs.04"
    "wms_title"     "h3286_0000.ihs.04"
  END
END
					

Single image layer

Openlayers: index.js

import "ol/ol.css";
import { Map, View } from "ol";
import TileLayer from "ol/layer/Tile";
import TileWMS from "ol/source/TileWMS";
import { defaults as defaultControls, ScaleLine } from "ol/control";
import { register } from "ol/proj/proj4";
import { Projection, getTransform, get } from "ol/proj";
import { getDistance } from "ol/sphere";
import proj4 from "proj4";

proj4.defs("EPSG:49900", "+proj=longlat +a=3396000 +b=3396000 +no_defs ");
proj4.defs("EPSG:49910", "+proj=eqc +lat_ts=0 +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +a=3396000 +b=3396000 +units=m +no_defs ");
register(proj4);

var projection = new Projection({
  code: "EPSG:49910",
  global: true,
  extent: [-10668848.652, -5215881.563, 10668848.652, 5215881.563],
  getPointResolution: function(resolution, point) {
    var toEPSG49900 = getTransform(get("EPSG:49910"), get("EPSG:49900"));
    var vertices = [
      point[0] - resolution / 2, point[1],
      point[0] + resolution / 2, point[1]
    ];
    vertices = toEPSG49900(vertices, vertices, 2);
    return getDistance(vertices.slice(0, 2), vertices.slice(2, 4), 3396000);
  }
});

const map = new Map({
  target: "map",
  layers: [
    new TileLayer({
      source: new TileWMS({
        url: "https://maps.planet.fu-berlin.de/eqc-bin/wms?",
        params: { LAYERS: "MOLA-gray-hs" }
      })
    }),
    new TileLayer({
      source: new TileWMS({
        url: "https://maps.planet.fu-berlin.de/eqc-bin/wms?",
        params: { LAYERS: "h3286_0000.ihs.04" }
      })
    })
  ],
  controls: defaultControls().extend([
    new ScaleLine({
      units: "metric"
    })
  ]),
  view: new View({
    center: [0, 0],
    zoom: 0,
    projection: projection
  })
});
                      

Dynamic single image

MapScript: productid.py

import mapscript

req = mapscript.OWSRequest()
req.loadParams()

productid = req.getValueByName('PRODUCTID')
layer = req.getValueByName('LAYERS')

map = mapscript.mapObj( 'eqc-dyn.map' )
maplayer = map.getLayerByName(layer)
maplayer.data = str(maplayer.data) + str(productid) + '.tif'

map.OWSDispatch( req )
                      

Dynamic single image layer

Openlayers: index.js

...

const map = new Map({
  target: "map",
  layers: [
    new TileLayer({
      source: new TileWMS({
        url: "https://maps.planet.fu-berlin.de/eqc-bin/wms?",
        params: { LAYERS: "MOLA-gray-hs" }
      })
    }),
    new TileLayer({
      source: new TileWMS({
        url: "https://maps.planet.fu-berlin.de/eqc-bin/productid.py?",
        params: { LAYERS: "hrsc4ihs", PRODUCTID: "h3286_0000.ihs.04" }
      })
    })
  ],
  controls: defaultControls().extend([
    new ScaleLine({
      units: "metric"
    })
  ]),
  view: new View({
    center: [0, 0],
    zoom: 0,
    projection: projection
  })
});
                      

MapCache

MapCache config.xml

  
      
         
            image/png
            MOLA-gray-hs
            true
         
      
      
         https://maps.planet.fu-berlin.de/eqc-bin/wms
      
   
   
      MOLA-gray-hs</source>
      disk
      eqc
      PNGQ_FAST
      5 5
      10
      3600
   
                      

MapCache

MapCache config.xml

   
      
         
            image/png
            hrsc4ihs
         
      
      
         https://maps.planet.fu-berlin.de/eqc-bin/productid.py
      
   
   
      hrsc4ihs
      tmpl
      eqc
      3600
      86400
      
       ^[a-zA-Z0-9\._]*$
      
   
                      

MapCache

MapCache config.xml

   
      
         Mars Equirectangular Sphere 3396000
      
      true
      EPSG:49910
      IAU2000:49910
      256 256
      m
      -10668848.652 -5215881.563 10668848.652 5215881.563
      41675.190046875 20837.5950234375 10418.79751171875 5209.398755859375 2604.6993779296877 1302.3496889648438 651.1748444824219 325.58742224121096 162.79371112060548 81.39685556030274 40.69842778015137 20.349213890075685 10.174606945 5.087303472 2.543651736
   
                      

MapCache

OpenLayers: index.js

import "ol/ol.css";
import { Map, View } from "ol";
import TileLayer from "ol/layer/Tile";
import TileWMS from "ol/source/TileWMS";
import { defaults as defaultControls, ScaleLine } from "ol/control";
import { register } from "ol/proj/proj4";
import { Projection, getTransform, get } from "ol/proj";
import { getDistance } from "ol/sphere";
import proj4 from "proj4";

proj4.defs("IAU2000:49900", "+proj=longlat +a=3396000 +b=3396000 +no_defs ");
proj4.defs(
  "IAU2000:49910",
  "+proj=eqc +lat_ts=0 +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +a=3396000 +b=3396000 +units=m +no_defs "
);
register(proj4);

var projection = new Projection({
  code: "IAU2000:49910",
  global: true,
  extent: [-10668848.652, -5215881.563, 10668848.652, 5215881.563],
  getPointResolution: function(resolution, point) {
    var toEPSG49900 = getTransform(get("IAU2000:49910"), get("IAU2000:49900"));
    var vertices = [
      point[0] - resolution / 2,
      point[1],
      point[0] + resolution / 2,
      point[1]
    ];
    vertices = toEPSG49900(vertices, vertices, 2);
    return getDistance(vertices.slice(0, 2), vertices.slice(2, 4), 3396000);
  }
});

const map = new Map({
  target: "map",
  layers: [
    new TileLayer({
      source: new TileWMS({
        url: "https://maps.planet.fu-berlin.de/eqc/?",
        params: { LAYERS: "MOLA-gray-hs" }
      })
    }),
    new TileLayer({
      source: new TileWMS({
        url: "https://maps.planet.fu-berlin.de/eqc/?",
        params: { LAYERS: "hrsc4ihs", PRODUCTID: "h3286_0000.ihs.04" }
      })
    })
  ],
  controls: defaultControls().extend([
    new ScaleLine({
      units: "metric"
    })
  ]),
  view: new View({
    center: [0, 0],
    zoom: 0,
    projection: projection
  })
});
                      

Mapscript as client

OWSLib