diff --git a/.gitignore b/.gitignore index 77a0f0c..9955bdb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ -workshop/content/site -.vs/ -# /notes.txt -workshop/exercises/postgis-data -notes.ignore +workshop/content/site +.vs/ +# /notes.txt +workshop/exercises/postgis-data +notes.ignore +/workshop/exercises/mapfiles/tmp/* +!/workshop/exercises/mapfiles/tmp/.gitkeep diff --git a/docker/ReadMe.md b/docker/ReadMe.md index 79df47f..a9c9516 100644 --- a/docker/ReadMe.md +++ b/docker/ReadMe.md @@ -47,9 +47,9 @@ docker pull geographika/mapserver-workshop:latest # docker stop mapserver-workshop # docker rm mapserver-workshop docker run -it --name mapserver-workshop geographika/mapserver-workshop:latest bash -docker start mapserver-workshop -docker exec -it mapserver-workshop bash # mapserv -v +# docker start mapserver-workshop +# docker exec -it mapserver-workshop bash ``` ## Build the Demo Image diff --git a/workshop/content/docs/advanced/direction.md b/workshop/content/docs/advanced/direction.md new file mode 100644 index 0000000..d181990 --- /dev/null +++ b/workshop/content/docs/advanced/direction.md @@ -0,0 +1,8 @@ + + +```bash +docker exec -it mapserver /bin/bash +map2img -m direction.map -o direction.png + +map2img -m direction.map -e -1133569.01 6841714.56 -1133466.97 6841780.95 -l "raster flow" -o direction.png +``` diff --git a/workshop/content/docs/advanced/gdalg.md b/workshop/content/docs/advanced/gdalg.md index 78b1dca..6285087 100644 --- a/workshop/content/docs/advanced/gdalg.md +++ b/workshop/content/docs/advanced/gdalg.md @@ -5,7 +5,7 @@ MapServer can dynamically run a [GDAL Vector Pipeline](https://gdal.org/en/latest/programs/gdal_vector_pipeline.html), and render its output - all through a simple Mapfile. -In this workshop we'll use the Tartu roads dataset used in the [Line Styling](../mapfile/lines.md) exercise, dynamically +In this workshop we'll use the Tartu roads dataset used in the [Line Styling](/mapfile/lines/) exercise, dynamically buffer it using GDAL, and display the result in OpenLayers using a MapServer WMS. This is a simple example of a pipeline, but additional steps can be chained together to create more complex workflows. diff --git a/workshop/content/docs/advanced/other-projections.md b/workshop/content/docs/advanced/other-projections.md index 1640661..9f7b252 100644 --- a/workshop/content/docs/advanced/other-projections.md +++ b/workshop/content/docs/advanced/other-projections.md @@ -189,7 +189,7 @@ const wmsLayer = new ImageLayer({ !!! note - OpenLayers does not natively support WCS. In this example we use the same approach as in the [Web Coverage Services (WCS)](../outputs/wcs.md) tutorial for testing the WCS protocol. + OpenLayers does not natively support WCS. In this example we use the same approach as in the [Web Coverage Services (WCS)](/outputs/wcs/) tutorial for testing the WCS protocol. Images are requested as PNGs using the [ImageWMS](https://openlayers.org/en/latest/apidoc/module-ol_source_ImageWMS-ImageWMS.html) class and a custom [imageLoadFunction](https://openlayers.org/en/latest/apidoc/module-ol_Image.html#~LoadFunction), as displaying GeoTIFFs directly in OpenLayers is not supported without additional libraries. diff --git a/workshop/content/docs/advanced/raster-pipeline.md b/workshop/content/docs/advanced/raster-pipeline.md new file mode 100644 index 0000000..c11dd4f --- /dev/null +++ b/workshop/content/docs/advanced/raster-pipeline.md @@ -0,0 +1,16 @@ +# GDAL Raster Pipeline + +!!! warning + + This page is currently in a draft form. + + +``` +gdal raster info data/raster/clipped.tif + +gdal raster info data/raster/clipped.gdalg.json + + +gdal raster pipeline ! read /etc/mapserver/data/raster/clipped.tif ! color-map --color-map /etc/mapserver/data/raster/color-map-percentage.txt ! blend --operator=hsv-value --overlay [ read /etc/mapserver/data/raster/clipped.tif ! hillshade -z 30 ] ! write /etc/mapserver/data/raster/tmp.tif --overwrite + +``` \ No newline at end of file diff --git a/workshop/content/docs/advanced/symbols.md b/workshop/content/docs/advanced/symbols.md index 5fa825b..883d2ed 100644 --- a/workshop/content/docs/advanced/symbols.md +++ b/workshop/content/docs/advanced/symbols.md @@ -10,7 +10,7 @@ In this exercise we'll look at how [SYMBOL](https://mapserver.org/mapfile/symbol ## Vector Symbols -We've looked at `ELLIPSE`, and `TRUETYPE` symbols in the [Points Styling](../mapfile/points.md) exercise. In this example +We've looked at `ELLIPSE`, and `TRUETYPE` symbols in the [Points Styling](/mapfile/points/) exercise. In this example we'll be looking at [Vector Symbols](https://mapserver.org/mapfile/symbology/construction.html#symbols-of-type-vector-and-ellipse) - which use vector drawings to define the shape of a symbol. diff --git a/workshop/content/docs/assets/images/clipped.gdalg.svg b/workshop/content/docs/assets/images/clipped.gdalg.svg new file mode 100644 index 0000000..4c0694b --- /dev/null +++ b/workshop/content/docs/assets/images/clipped.gdalg.svg @@ -0,0 +1,98 @@ + + + + + + +GDALG Workflow + + +0 + + + +read + +/etc/mapserver/data/raster/clipped.tif + + + + + +1 + + + +color-map + +--color-map /etc/mapserver/data/raster/color-map-percentage.txt + + + + + +0->1 + + + + + +2 + + + +blend + +--operator hsv-value + +--overlay + + + + + +1->2 + + + + + +3 + + + +read + +/etc/mapserver/data/raster/clipped.tif + + + + + +4 + + + +hillshade + +-z 2 + + + + + +3->4 + + + + + +4->2 + + + + + diff --git a/workshop/content/docs/assets/images/filters.png b/workshop/content/docs/assets/images/filters.png new file mode 100644 index 0000000..66b0266 Binary files /dev/null and b/workshop/content/docs/assets/images/filters.png differ diff --git a/workshop/content/docs/assets/images/roads.gdalg.svg b/workshop/content/docs/assets/images/roads.gdalg.svg new file mode 100644 index 0000000..21659db --- /dev/null +++ b/workshop/content/docs/assets/images/roads.gdalg.svg @@ -0,0 +1,42 @@ + + + + + + +GDALG Workflow + + +0 + + + +read + +/etc/mapserver/data/osm/roads.fgb + + + + + +1 + + + +buffer + +--distance 0.0001 + + + + + +0->1 + + + + + diff --git a/workshop/content/docs/mapfile/config.md b/workshop/content/docs/mapfile/config.md new file mode 100644 index 0000000..aaa5ea4 --- /dev/null +++ b/workshop/content/docs/mapfile/config.md @@ -0,0 +1,16 @@ +# MapServer Configuration File + +## Overview + + +Set this in the config file + MS_ONLINERESOURCE "/" + + + Include the one used by the workshop + +Any changes in the CONFIG file you need to restart the web server: + +```bash +docker restart mapserver +``` \ No newline at end of file diff --git a/workshop/content/docs/outputs/ogcapi-features-query.md b/workshop/content/docs/outputs/ogcapi-features-query.md new file mode 100644 index 0000000..58a7133 --- /dev/null +++ b/workshop/content/docs/outputs/ogcapi-features-query.md @@ -0,0 +1,171 @@ +# OGCAPI Features Part 3 Filtering + +Filtering allows requests to an OGC API - Features service to return only the features needed. +This can be based on feature properties, location, or a combination of both. +In this tutorial, you'll learn how filtering is supported in MapServer and how to use it in a sample web application. + +
+ +
+ +Let's take a look at the [Timișoara OGC API Landing Page](http://localhost:7000/TIMISOARA/ogcapi/?f=html). +This contains a link to a page listing all the supported [Conformance Classes](http://localhost:7000/TIMISOARA/ogcapi/conformance?f=html). + +The three we are interested in for this tutorial are: + +- +- +- + +!!! note + + As this is an unreleased feature (due as part of the MapServer 8.8 release), documentation can only be seen on the [documentation preview](https://mapserver.github.io/MapServer-documentation/ogc/ogc_api.html) + web site. + +We can see which attributes in the **buildings** layer are filterable by opening the queryables endpoint: + + + +This list is controlled in MapServer by the `oga_queryable_items` keyword in the layer `METADATA` block. +In this example we use the special value `all`, which exposes all layer attributes as queryables +(similar to `gml_include_items` when set to `all`). If we only want selected attributes we can +use a comma-separated list. + +```scala + LAYER + NAME "buildings" + TYPE POLYGON + # allow the OGR FeatureId to be returned and used for querying + # see https://github.com/MapServer/MapServer/pull/7533 + PROCESSING "OGR_EXPOSE_FID=TRUE" + METADATA + "ows_title" "Buildings" + "gml_include_items" "all" + "gml_featureid" "fid" + "gml_types" "auto" + "oga_queryable_items" "all" + # or a list of properties + # "oga_queryable_items" "name,osm_id" + ... +``` + +The default MapServer Bootstrap templates then expose these queryables as simple filter inputs in the HTML interface. +You can try this directly on the items page: + + + +![Filter interface](../assets/images/filters.png) + +## Filter Types + +CQL2 stands for Common Query Language 2, an OGC standard for defining filters on feature data. + +CQL2-text and CQL2-JSON are two equivalent encodings of the same filtering language. CQL2-text is a +human-readable syntax. CQL2-JSON represents the same logic as a structured JSON object, +making it easier for software to generate, validate, and manipulate programmatically. + +When you use the Bootstrap template above and apply a filter, you will see the query in the URL, for example: + + + +The `filter-lang=cql2-text` parameter indicates that the filter is written in CQL2-text. +The filter itself is URL-encoded (for example spaces become `+` and special characters are percent-encoded), +which is required because it is being passed in a URL query string. + +We can also write the same query using CQL2-JSON: + +```json +{ + "op": "like", + "args": [ + { "property": "building" }, + "%hospital%" + ] +} +``` + +Both expressions mean the same thing: return features where the `building` attribute contains "hospital". + +The JSON filter looks as follows as an encoded-URL: + + + +If you open this in a browser, you will see a list of matching features. These are returned in JSON because we supplied the `f=json` parameter. +We also have to add the `&filter-lang=cql2-json` parameter, as by default MapServer assumes `cql2-text`. + +In addition to CQL2 filters, MapServer also supports simple query parameter filtering on queryable attributes. +For example, `&building=hospital` is a shorthand for an equality-style filter on the building attribute. +This is convenient for quick lookups but lacks the expressiveness of full CQL2. + + + +## Custom Application with Filtering + +MapServer's filtering support allows us to quickly build powerful applications, using +simple API calls. links to a custom OpenLayers application, +using the OGC filtering API to filter buildings by type, and highlight them on a map. + +The key parts of the code relating to filtering are building the filter string (as `cql2-text`): + +```js +const cqlFilter = `building = '${buildingType}'`; +return `${baseUrl}/timisoara/ogcapi/collections/buildings/items?` + + `filter=${encodeURIComponent(cqlFilter)}&filter-lang=cql2-text&limit=1000&f=json`; +``` + +And updating the data source when the selection changes: + +```js +document.getElementById('building-select').addEventListener('change', (e) => { + const newSource = new VectorSource({ + format: new GeoJSON(), + url: buildOgcUrl(e.target.value), + strategy: allStrategy, + }); + vectorLayer.setSource(newSource); +}); +``` + +The options for the drop-down list are defined in the `./workshop/exercises/app/timisoara.html` page. + +```html + +``` + +## Code + +!!! example + + - MapServer OGC API Features request: + - Local OpenLayers example: + +??? JavaScript "timisoara.js" + + ``` js + --8<-- "timisoara.js" + ``` + +??? Mapfile "ogcapi-features.map" + + ``` scala + --8<-- "timisoara.map" + ``` + +## Exercises + +- Update the `oga_queryable_items` metadata keyword to only include the "building" and "name" fields. + Check that the list of queryables is updated at: + , + and also in the Bootstrap page: + . + +- Add a new building type to the dropdown in `./workshop/exercises/app/timisoara.html`. + You can find additional types by inspecting the + [buildings items page](http://localhost:7000/TIMISOARA/ogcapi/collections/buildings/items?f=html). diff --git a/workshop/content/docs/summary.md b/workshop/content/docs/summary.md index 504eb80..8e3e8b4 100644 --- a/workshop/content/docs/summary.md +++ b/workshop/content/docs/summary.md @@ -23,6 +23,7 @@ MapServer community! Yearly summaries of developments in MapServer and the MapServer community can be found below: +- [State of MapServer 2026](https://geographika.github.io/mapserver-state-2026/) - [State of MapServer 2025](https://geographika.github.io/mapserver-state-2025/) - [State of MapServer 2024](https://geographika.github.io/mapserver-state-2024/) - [State of MapServer 2022](https://geographika.github.io/mapserver-state-2022/) \ No newline at end of file diff --git a/workshop/content/zensical.toml b/workshop/content/zensical.toml index f98ba0e..207efa4 100644 --- a/workshop/content/zensical.toml +++ b/workshop/content/zensical.toml @@ -2,7 +2,7 @@ site_name = "Getting Started with MapServer" site_description = "MapServer is an Open Source platform for publishing spatial data and interactive mapping applications to the web. This workshop will cover publishing geospatial data to the Web using MapServer in support of the suite of OGC API standards." site_author = "The MapServer community" -copyright = "© 2025 Seth Girvin and the MapServer community" +copyright = "© 2026 Seth Girvin and the MapServer community" site_url = "https://mapserver.github.io/getting-started-with-mapserver" repo_url = "https://github.com/mapserver/getting-started-with-mapserver" docs_dir = "docs" @@ -42,7 +42,8 @@ nav = [ { "WCS" = "outputs/wcs.md" }, { "Tiles" = "outputs/tiles.md" }, { "Vector Tiles" = "outputs/vector-tiles.md" }, - { "OGC API - Features" = "outputs/ogcapi-features.md" } + { "OGC API - Features" = "outputs/ogcapi-features.md" }, + { "OGC API - Filtering" = "outputs/ogcapi-features-query.md" } ]}, { "Advanced" = [ { "ArcGIS Feature Server" = "advanced/arcgis.md" }, diff --git a/workshop/exercises/app/css/style.css b/workshop/exercises/app/css/style.css index 10a86e8..2c091c5 100644 --- a/workshop/exercises/app/css/style.css +++ b/workshop/exercises/app/css/style.css @@ -13,4 +13,30 @@ html, body { .bw { filter: grayscale(100%); +} + +#loading-mask { + display: none; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.3); + z-index: 2000; + align-items: center; + justify-content: center; +} + + #loading-mask.active { + display: flex; + } + +#loading-spinner { + background: white; + padding: 12px 20px; + border-radius: 6px; + font-family: sans-serif; + font-size: 14px; + box-shadow: 0 2px 6px rgba(0,0,0,0.3); } \ No newline at end of file diff --git a/workshop/exercises/app/direction.html b/workshop/exercises/app/direction.html new file mode 100644 index 0000000..cfecfd5 --- /dev/null +++ b/workshop/exercises/app/direction.html @@ -0,0 +1,14 @@ + + + + + + + + Direction + + +
+ + + diff --git a/workshop/exercises/app/index.html b/workshop/exercises/app/index.html index f04a9f9..4c47800 100644 --- a/workshop/exercises/app/index.html +++ b/workshop/exercises/app/index.html @@ -30,6 +30,7 @@

Outputs

  • Tiles Mode
  • Vector Tiles
  • OGC API - Features
  • +
  • OGC API - Filtering
  • Advanced

      diff --git a/workshop/exercises/app/js/direction.js b/workshop/exercises/app/js/direction.js new file mode 100644 index 0000000..a8d1647 --- /dev/null +++ b/workshop/exercises/app/js/direction.js @@ -0,0 +1,33 @@ +import '../css/style.css'; +import ImageWMS from 'ol/source/ImageWMS.js'; +import Map from 'ol/Map.js'; +import OSM from 'ol/source/OSM.js'; +import View from 'ol/View.js'; +import { Image as ImageLayer, Tile as TileLayer } from 'ol/layer.js'; + +const mapserverUrl = import.meta.env.VITE_MAPSERVER_BASE_URL; +const mapfilesPath = import.meta.env.VITE_MAPFILES_PATH; + +const layers = [ + new TileLayer({ + source: new OSM(), + }), + new ImageLayer({ + extent: [-1136368.0, 6840385.0, -1131379.0, 6843727.0], + source: new ImageWMS({ + url: mapserverUrl + mapfilesPath + 'direction.map&', + // can also add a raster layer + params: { 'LAYERS': 'flow', 'STYLES': '' }, + ratio: 1 + }), + }), +]; +const map = new Map({ + layers: layers, + target: 'map', + view: new View({ + center: [-1133873.5, 6842056.0], + zoom: 14, + }), +}); + diff --git a/workshop/exercises/app/js/timisoara.js b/workshop/exercises/app/js/timisoara.js new file mode 100644 index 0000000..f545c6f --- /dev/null +++ b/workshop/exercises/app/js/timisoara.js @@ -0,0 +1,78 @@ +import '../css/style.css'; +import ImageWMS from 'ol/source/ImageWMS.js'; +import Map from 'ol/Map.js'; +import OSM from 'ol/source/OSM.js'; +import View from 'ol/View.js'; +import { Image as ImageLayer, Tile as TileLayer } from 'ol/layer.js'; +import VectorLayer from 'ol/layer/Vector.js'; +import VectorSource from 'ol/source/Vector.js'; +import GeoJSON from 'ol/format/GeoJSON.js'; +import { Style, Stroke } from 'ol/style.js'; +import { all as allStrategy } from 'ol/loadingstrategy.js'; + +const mapserverUrl = import.meta.env.VITE_MAPSERVER_BASE_URL; +const mapfilesPath = import.meta.env.VITE_MAPFILES_PATH; +const baseUrl = mapserverUrl.replace(/\?+$/, ''); + +function buildOgcUrl(buildingType) { + const cqlFilter = `building = '${buildingType}'`; + return `${baseUrl}/timisoara/ogcapi/collections/buildings/items?` + + `filter=${encodeURIComponent(cqlFilter)}&filter-lang=cql2-text&limit=1000&f=json`; +} + +const select = document.getElementById('building-select'); + +const vectorSource = new VectorSource({ + format: new GeoJSON(), + url: buildOgcUrl(select.value), + strategy: allStrategy, +}); + +const vectorLayer = new VectorLayer({ + source: vectorSource, + style: [ + new Style({ + stroke: new Stroke({ color: 'rgba(0,0,0,0.5)', width: 5 }), + }), + new Style({ + stroke: new Stroke({ color: 'cyan', width: 3 }), + }), + ] +}); + +const layers = [ + new TileLayer({ + source: new OSM(), + visible: false, + }), + new ImageLayer({ + opacity: 0.2, + source: new ImageWMS({ + url: mapserverUrl + mapfilesPath + 'timisoara.map&', + params: { 'LAYERS': 'buildings', 'STYLES': '' }, + }), + }), + vectorLayer, +]; + +const map = new Map({ + layers: layers, + target: 'map', + view: new View({ + center: [2363111, 5740066], + zoom: 16, + }), +}); + +document.getElementById('building-select').addEventListener('change', (e) => { + const newSource = new VectorSource({ + format: new GeoJSON(), + url: buildOgcUrl(e.target.value), + strategy: allStrategy, + }); + vectorLayer.setSource(newSource); +}); + +const loadingMask = document.getElementById('loading-mask'); +map.on('loadstart', () => loadingMask.classList.add('active')); +map.on('loadend', () => loadingMask.classList.remove('active')); \ No newline at end of file diff --git a/workshop/exercises/app/timisoara.html b/workshop/exercises/app/timisoara.html new file mode 100644 index 0000000..335910e --- /dev/null +++ b/workshop/exercises/app/timisoara.html @@ -0,0 +1,47 @@ + + + + + + + + Timișoara + + + +
      +
      Loading...
      +
      +
      +
      + + +
      + + + \ No newline at end of file diff --git a/workshop/exercises/mapfiles/data/osm/roads.gdalg.json b/workshop/exercises/mapfiles/data/osm/roads.gdalg.json new file mode 100644 index 0000000..9eddea3 --- /dev/null +++ b/workshop/exercises/mapfiles/data/osm/roads.gdalg.json @@ -0,0 +1,4 @@ +{ + "type": "gdal_streamed_alg", + "command_line": "gdal vector pipeline ! read /etc/mapserver/data/osm/roads.fgb ! buffer --distance=0.0001" +} \ No newline at end of file diff --git a/workshop/exercises/mapfiles/data/osm/timisoara/buildings.gpkg b/workshop/exercises/mapfiles/data/osm/timisoara/buildings.gpkg new file mode 100644 index 0000000..bc21a5f Binary files /dev/null and b/workshop/exercises/mapfiles/data/osm/timisoara/buildings.gpkg differ diff --git a/workshop/exercises/mapfiles/data/raster/clipped.gdalg.json b/workshop/exercises/mapfiles/data/raster/clipped.gdalg.json new file mode 100644 index 0000000..60ddd11 --- /dev/null +++ b/workshop/exercises/mapfiles/data/raster/clipped.gdalg.json @@ -0,0 +1,4 @@ +{ + "type": "gdal_streamed_alg", + "command_line": "gdal raster pipeline ! read /etc/mapserver/data/raster/clipped.tif ! color-map --color-map /etc/mapserver/data/raster/color-map-percentage.txt ! blend --operator=hsv-value --overlay [ read /etc/mapserver/data/raster/clipped.tif ! hillshade -z 2 ] " +} \ No newline at end of file diff --git a/workshop/exercises/mapfiles/data/raster/clipped.tif b/workshop/exercises/mapfiles/data/raster/clipped.tif new file mode 100644 index 0000000..5d1c10e Binary files /dev/null and b/workshop/exercises/mapfiles/data/raster/clipped.tif differ diff --git a/workshop/exercises/mapfiles/data/raster/clipped.tif.aux.xml b/workshop/exercises/mapfiles/data/raster/clipped.tif.aux.xml new file mode 100644 index 0000000..80c312d --- /dev/null +++ b/workshop/exercises/mapfiles/data/raster/clipped.tif.aux.xml @@ -0,0 +1,11 @@ + + + + 488.14999389648 + 1247.4100341797 + 800.38911958498 + 178.88367477306 + 48.09 + + + diff --git a/workshop/exercises/mapfiles/data/raster/color-map-percentage.txt b/workshop/exercises/mapfiles/data/raster/color-map-percentage.txt new file mode 100644 index 0000000..37b9e63 --- /dev/null +++ b/workshop/exercises/mapfiles/data/raster/color-map-percentage.txt @@ -0,0 +1,12 @@ +nv 255 255 255 +0% 112 147 141 +10% 120 159 152 +20% 130 165 159 +30% 145 177 171 +40% 180 192 180 +50% 255 201 180 +60% 212 184 163 +70% 212 193 179 +80% 212 207 204 +90% 220 220 220 +100% 235 235 237 diff --git a/workshop/exercises/mapfiles/direction.map b/workshop/exercises/mapfiles/direction.map new file mode 100644 index 0000000..61ca1f9 --- /dev/null +++ b/workshop/exercises/mapfiles/direction.map @@ -0,0 +1,149 @@ +MAP + NAME "Direction" + EXTENT -1136368 6840385 -1131379 6843727 + SIZE 800 800 + IMAGETYPE "PNG24" + PROJECTION + "epsg:3857" + END + WEB + IMAGEPATH "/etc/mapserver/tmp/" + METADATA + "ows_enable_request" "*" + "ows_srs" "EPSG:3857 EPSG:4326" + END + END + SYMBOL + NAME "arrow" + TYPE VECTOR + FILLED TRUE + POINTS + 0 0 + 5 10 + 10 0 + 7 0 + 7 -10 + 3 -10 + 3 0 + 0 0 + END + END + LAYER + NAME "raster" + # TYPE RASTER + STATUS OFF + DATA "data/raster/flow.tif" + PROCESSING "BANDS=1" + TYPE POINT + CONNECTIONTYPE RASTERLABEL + PROCESSING "RESAMPLE=NEAREST" + CLASS + TEXT (tostring([value],"%.0f")) + LABEL + TYPE TRUETYPE + COLOR 50 50 50 + SIZE 8 + END + END + END + + LAYER + NAME "flow" + STATUS OFF + TYPE POINT + CONNECTIONTYPE UVRASTER + DATA "data/raster/flow.tif" + PROCESSING "BANDS=1,1" + # PROCESSING "UV_SPACING=32" + # PROCESSING "UV_SIZE_SCALE=1" + PROCESSING "RESAMPLE=NEAREST" + # CLASSITEM "[u]" # does not seem to be available + + # MINSCALEDENOM 5000 + # MAXSCALEDENOM 50000 + + # https://grass.osgeo.org/grass-stable/manuals/r.watershed.html + # Drainage is 8 directions numbered counter-clockwise starting from 1 in north-east direction + + # 2 = North + CLASS + EXPRESSION ([u] = 2 OR [u] = -2) + STYLE + SYMBOL "arrow" + SIZE 12 + ANGLE 0 + COLOR "#483D8B" + END + END + # 1 = North-East + CLASS + EXPRESSION ([u] = 1 OR [u] = -1) + STYLE + SYMBOL "arrow" + SIZE 12 + ANGLE 45 + COLOR "#483D8B" + END + END + # 8 = East + CLASS + EXPRESSION ([u] = 8 OR [u] = -8) + STYLE + SYMBOL "arrow" + SIZE 12 + ANGLE 90 + COLOR "#483D8B" + END + END + # 7 = South-East + CLASS + EXPRESSION ([u] = 7 OR [u] = -7) + STYLE + SYMBOL "arrow" + SIZE 12 + ANGLE 135 + COLOR "#483D8B" + END + END + # 6 = South + CLASS + EXPRESSION ([u] = 6 OR [u] = -6) + STYLE + SYMBOL "arrow" + SIZE 12 + ANGLE 180 + COLOR "#483D8B" + END + END + # 5 = South-West + CLASS + EXPRESSION ([u] = 5 OR [u] = -5) + STYLE + SYMBOL "arrow" + SIZE 12 + ANGLE 225 + COLOR "#483D8B" + END + END + # 4 = West + CLASS + EXPRESSION ([u] = 4 OR [u] = -4) + STYLE + SYMBOL "arrow" + SIZE 12 + ANGLE 270 + COLOR "#483D8B" + END + END + # 3 = North-West + CLASS + EXPRESSION ([u] = 3 OR [u] = -3) + STYLE + SYMBOL "arrow" + SIZE 12 + ANGLE 315 + COLOR "#483D8B" + END + END + END +END \ No newline at end of file diff --git a/workshop/exercises/mapfiles/gdalg.map b/workshop/exercises/mapfiles/gdalg.map index d24011d..cea72eb 100644 --- a/workshop/exercises/mapfiles/gdalg.map +++ b/workshop/exercises/mapfiles/gdalg.map @@ -4,7 +4,7 @@ MAP SIZE 800 600 IMAGECOLOR "#0A1E50" PROJECTION - "init=epsg:4326" + "epsg:4326" END WEB IMAGEPATH "/etc/mapserver/tmp/" @@ -25,10 +25,9 @@ MAP STATUS OFF CONNECTIONTYPE OGR # note we need to use the full path to the dataset using the inline pipeline - CONNECTION '{"type": "gdal_streamed_alg","command_line": "gdal vector pipeline ! read /etc/mapserver/data/osm/roads.fgb ! geom buffer --distance=0.0001"}' + CONNECTION '{"type": "gdal_streamed_alg","command_line": "gdal vector pipeline ! read /etc/mapserver/data/osm/roads.fgb ! buffer --distance=0.0001"}' + # CONNECTION "data/osm/roads.gdalg.json" DATA "0" - # CONNECTION "roads.gdalg.json" - # DATA "0" CLASS STYLE COLOR 50 100 180 @@ -36,7 +35,7 @@ MAP STYLE SYMBOL "hatchsymbol" COLOR "#78C8FF" - WIDTH 0.1 + WIDTH 0.3 ANGLE 45 SIZE 8 END diff --git a/workshop/exercises/mapfiles/mapserver.conf b/workshop/exercises/mapfiles/mapserver.conf index 04a0e42..4bf0c8a 100644 --- a/workshop/exercises/mapfiles/mapserver.conf +++ b/workshop/exercises/mapfiles/mapserver.conf @@ -81,6 +81,7 @@ CONFIG POSTGIS "/etc/mapserver/postgis.map" RAILWAYS "/etc/mapserver/railways.map" RASTER "/etc/mapserver/raster.map" + RASTER-PIPELINE "/etc/mapserver/raster-pipeline.map" SLD "/etc/mapserver/sld.map" STAC "/etc/mapserver/stac.map" STARS "/etc/mapserver/stars.map" diff --git a/workshop/exercises/mapfiles/raster-pipeline.map b/workshop/exercises/mapfiles/raster-pipeline.map new file mode 100644 index 0000000..4e81286 --- /dev/null +++ b/workshop/exercises/mapfiles/raster-pipeline.map @@ -0,0 +1,40 @@ +MAP + NAME "RASTER-PIPELINE" + IMAGETYPE "png" + MAXSIZE 4096 + SIZE 800 800 + EXTENT 977816 6742006 998431 6755017 + WEB + IMAGEPATH "/etc/mapserver/tmp/" + END + + PROJECTION + "EPSG:2154" + END + + IMAGECOLOR 255 255 255 + + OUTPUTFORMAT + NAME "png" + DRIVER "AGG/PNG" + MIMETYPE "image/png" + IMAGEMODE RGBA + EXTENSION "png" + TRANSPARENT ON + END + + LAYER + NAME "dtm-pipeline" + EXTENT 977816 6742006 998431 6755017 + STATUS DEFAULT + TYPE RASTER + PROCESSING "NODATA=0" # set NoData to empty + # Map data: IGN + # note we need to use the full path to the dataset using the inline pipeline + DATA '{"type": "gdal_streamed_alg","command_line": "gdal raster pipeline ! read /etc/mapserver/data/raster/clipped.tif ! color-map --color-map /etc/mapserver/data/raster/color-map-percentage.txt ! blend --operator=hsv-value --overlay [ read /etc/mapserver/data/raster/clipped.tif ! hillshade -z 2 ] "}' + # DATA "data/raster/clipped.gdalg.json" + PROJECTION + "EPSG:2154" + END + END +END \ No newline at end of file diff --git a/workshop/exercises/mapfiles/roads.gdalg.json b/workshop/exercises/mapfiles/roads.gdalg.json deleted file mode 100644 index 8884047..0000000 --- a/workshop/exercises/mapfiles/roads.gdalg.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "type": "gdal_streamed_alg", - "command_line": "gdal vector pipeline ! read data/osm/roads.fgb ! geom buffer --distance=0.0001" -} \ No newline at end of file diff --git a/workshop/exercises/mapfiles/symbols.sym b/workshop/exercises/mapfiles/symbols.sym new file mode 100644 index 0000000..7c2e45a --- /dev/null +++ b/workshop/exercises/mapfiles/symbols.sym @@ -0,0 +1,10 @@ +SYMBOLSET + SYMBOL + NAME "circle" + TYPE ELLIPSE + FILLED TRUE + POINTS + 1 1 + END + END +END \ No newline at end of file diff --git a/workshop/exercises/mapfiles/timisoara.map b/workshop/exercises/mapfiles/timisoara.map new file mode 100644 index 0000000..9ec9e11 --- /dev/null +++ b/workshop/exercises/mapfiles/timisoara.map @@ -0,0 +1,204 @@ +MAP + NAME "Timisoara" + SIZE 800 800 + EXTENT 20.9984994 45.6280885 21.5152955 45.8955907 + PROJECTION + "EPSG:4326" + END + WEB + IMAGEPATH "/etc/mapserver/tmp/" # end-slash is important! + METADATA + "ows_enable_request" "*" + "ows_srs" "EPSG:4326 EPSG:3857" + "ows_use_default_extent_for_getfeature" "false" + END + END + LAYER + NAME "buildings" + PROJECTION + "EPSG:4326" + END + STATUS OFF + TYPE POLYGON + PROCESSING "OGR_EXPOSE_FID=TRUE" + CONNECTIONTYPE OGR + CONNECTION "data/osm/timisoara/buildings.gpkg" + TEMPLATE "ttt" + METADATA + "ows_title" "Buildings" + "gml_include_items" "all" + "gml_featureid" "fid" + "gml_types" "auto" + "oga_queryable_items" "all" + "oga_sortable_items" "all" + END + CLASSITEM "building" + CLASS + STYLE + COLOR 220 80 60 + END + EXPRESSION {house} + END + CLASS + STYLE + COLOR 60 130 220 + END + EXPRESSION {apartments} + END + CLASS + STYLE + COLOR 180 100 40 + END + EXPRESSION {industrial} + END + CLASS + STYLE + COLOR 100 180 80 + END + EXPRESSION {residential} + END + CLASS + STYLE + COLOR 240 160 60 + END + EXPRESSION {detached} + END + CLASS + STYLE + COLOR 130 110 170 + END + EXPRESSION {garages} + END + CLASS + STYLE + COLOR 150 125 185 + END + EXPRESSION {garage} + END + CLASS + STYLE + COLOR 60 190 190 + END + EXPRESSION {commercial} + END + CLASS + STYLE + COLOR 210 140 90 + END + EXPRESSION {semidetached_house} + END + CLASS + STYLE + COLOR 80 160 200 + END + EXPRESSION {school} + END + CLASS + STYLE + COLOR 160 140 110 + END + EXPRESSION {warehouse} + END + CLASS + STYLE + COLOR 190 190 100 + END + EXPRESSION {roof} + END + CLASS + STYLE + COLOR 100 200 120 + END + EXPRESSION {greenhouse} + END + CLASS + STYLE + COLOR 220 110 150 + END + EXPRESSION {retail} + END + CLASS + STYLE + COLOR 200 160 80 + END + EXPRESSION {construction} + END + CLASS + STYLE + COLOR 180 120 100 + END + EXPRESSION {terrace} + END + CLASS + STYLE + COLOR 170 130 200 + END + EXPRESSION {church} + END + CLASS + STYLE + COLOR 240 180 130 + END + EXPRESSION {kindergarten} + END + CLASS + STYLE + COLOR 200 80 100 + END + EXPRESSION {hospital} + END + CLASS + STYLE + COLOR 80 110 190 + END + EXPRESSION {university} + END + CLASS + STYLE + COLOR 120 170 210 + END + EXPRESSION {dormitory} + END + CLASS + STYLE + COLOR 160 155 140 + END + EXPRESSION {shed} + END + CLASS + STYLE + COLOR 90 150 170 + END + EXPRESSION {office} + END + CLASS + STYLE + COLOR 140 200 160 + END + EXPRESSION {public} + END + CLASS + STYLE + COLOR 170 165 150 + END + EXPRESSION {service} + END + CLASS + STYLE + COLOR 100 100 160 + END + EXPRESSION {train_station} + END + CLASS + STYLE + COLOR 210 150 170 + END + EXPRESSION {hotel} + END + CLASS + STYLE + COLOR 220 220 220 + END + END + END +END \ No newline at end of file diff --git a/workshop/exercises/mapfiles/tmp/.gitkeep b/workshop/exercises/mapfiles/tmp/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workshop/exercises/scripts/diagrams.ps1 b/workshop/exercises/scripts/diagrams.ps1 new file mode 100644 index 0000000..a927f77 --- /dev/null +++ b/workshop/exercises/scripts/diagrams.ps1 @@ -0,0 +1,6 @@ +cd D:\GitHub\getting-started-with-mapserver +pip install gdalgviz +$GVIZ_PATH = "C:\Program Files\Graphviz\bin" +$env:PATH = "$GVIZ_PATH;$env:PATH" +gdalgviz ./workshop/exercises/mapfiles/data/raster/clipped.gdalg.json ./workshop/content/docs/assets/images/clipped.gdalg.svg --header-color "#33A333" --node-attr "fontsize=14,fontname=Arial" --graph-attr "bgcolor=transparent,pad=0.5" +gdalgviz ./workshop/exercises/mapfiles/data/osm/roads.gdalg.json ./workshop/content/docs/assets/images/roads.gdalg.svg --header-color "#33A333" --node-attr "fontsize=14,fontname=Arial" --graph-attr "bgcolor=transparent,pad=0.5"