diff --git a/.github/workflows/deploy.app.yml b/.github/workflows/deploy.app.yml index 2fa4720..a5e7b09 100644 --- a/.github/workflows/deploy.app.yml +++ b/.github/workflows/deploy.app.yml @@ -17,7 +17,7 @@ jobs: name: Build and Deploy JavaScript application runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Build website run: | npm install diff --git a/.github/workflows/deploy.docs.yml b/.github/workflows/deploy.docs.yml index 1f9f8b5..42621b0 100644 --- a/.github/workflows/deploy.docs.yml +++ b/.github/workflows/deploy.docs.yml @@ -1,28 +1,40 @@ -name: Deploy documentation ⚙️ - -on: - push: - branches: - - main - paths: - - 'workshop/content/**' - -defaults: - run: - working-directory: workshop/content - -jobs: - build: - name: Build and Deploy Documentation - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Install requirements 📦 - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - - name: Deploy 📦 - run: mkdocs gh-deploy --strict --force --message 'update website via GitHub Actions' +name: Deploy documentation ⚙️ + +on: + push: + branches: + - main + paths: + - 'workshop/content/**' + +jobs: + build: + name: Build and Deploy Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: '3.x' + - name: Install requirements 📦 + run: | + python -m pip install --upgrade pip + pip install -r workshop/content/requirements.txt + - name: Build site 📦 + run: | + zensical build --clean --strict + mv site /tmp/ + - name: checkout gh-pages branch + uses: actions/checkout@master + with: + ref: gh-pages + - name: Deploy to GitHub Pages 📦 + run: | + git checkout gh-pages + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + ls | grep -v ".nojekyll"| xargs rm -fr + mv -f /tmp/site/* . + git add . + git commit -am "update website via GitHub Actions" + git push diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5c274bc..96d5b6e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,26 +1,26 @@ -name: Test documentation ⚙️ - -on: - pull_request: - paths: - - 'workshop/content/**' - -defaults: - run: - working-directory: workshop/content - -jobs: - build: - name: Test documentation build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - with: - python-version: '3.x' - - name: Install requirements 📦 - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - - name: Deploy 📦 - run: mkdocs build --strict +name: Test documentation ⚙️ + +on: + pull_request: + paths: + - 'workshop/content/**' + +defaults: + run: + working-directory: workshop/content + +jobs: + build: + name: Test documentation build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: '3.x' + - name: Install requirements 📦 + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Deploy 📦 + run: zensical build --clean --strict diff --git a/README.md b/README.md index 25e2c0f..24190fb 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ the [Workshop environment setup](https://mapserver.github.io/getting-started-wit ### Building the workshop content locally -The workshop manual is powered by [MkDocs](https://www.mkdocs.org) which facilitates easy management +The workshop manual is powered by [Zensical](https://zensical.org) which facilitates easy management of content and publishing. Workshop content is written in Markdown. @@ -43,9 +43,9 @@ cd getting-started-with-mapserver/workshop/content # install required dependencies pip install -r requirements.txt # build the website -mkdocs build +zensical build # serve locally -mkdocs serve # website is made available on http://localhost:8000 +zensical serve # website is made available on http://localhost:8000 ``` ## Contributing updates @@ -56,10 +56,4 @@ Changes to the GitHub repository result in an automated build and deploy of the ## Deploying to live site -Website updates are automatically published via GitHub Actions. To publish manually: - -```bash -# NOTE: you require access privileges to the GitHub repository -# to publish live updates -mkdocs gh-deploy -m 'add new page on topic x' -``` +Website updates are automatically published via GitHub Actions. diff --git a/workshop/content/docs/assets/javascripts/custom.js b/workshop/content/docs/assets/javascripts/custom.js deleted file mode 100644 index 45243eb..0000000 --- a/workshop/content/docs/assets/javascripts/custom.js +++ /dev/null @@ -1,105 +0,0 @@ -document.querySelectorAll(".use-termynal").forEach(node => { - node.style.display = "block"; - new Termynal(node, { - lineDelay: 500 - }); -}); -const progressLiteralStart = "---> 100%"; -const promptLiteralStart = "$ "; -const customPromptLiteralStart = "# "; -const termynalActivateClass = "termy"; -let termynals = []; - -function createTermynals() { - document - .querySelectorAll(`.${termynalActivateClass} .highlight`) - .forEach(node => { - const text = node.textContent; - const lines = text.split("\n"); - const useLines = []; - let buffer = []; - function saveBuffer() { - if (buffer.length) { - let isBlankSpace = true; - buffer.forEach(line => { - if (line) { - isBlankSpace = false; - } - }); - dataValue = {}; - if (isBlankSpace) { - dataValue["delay"] = 0; - } - if (buffer[buffer.length - 1] === "") { - // A last single
won't have effect - // so put an additional one - buffer.push(""); - } - const bufferValue = buffer.join("
"); - dataValue["value"] = bufferValue; - useLines.push(dataValue); - buffer = []; - } - } - for (let line of lines) { - if (line === progressLiteralStart) { - saveBuffer(); - useLines.push({ - type: "progress" - }); - } else if (line.startsWith(promptLiteralStart)) { - saveBuffer(); - const value = line.replace(promptLiteralStart, "").trimEnd(); - useLines.push({ - type: "input", - value: value - }); - } else if (line.startsWith("// ")) { - saveBuffer(); - const value = "💬 " + line.replace("// ", "").trimEnd(); - useLines.push({ - value: value, - class: "termynal-comment", - delay: 0 - }); - } else if (line.startsWith(customPromptLiteralStart)) { - saveBuffer(); - const promptStart = line.indexOf(promptLiteralStart); - if (promptStart === -1) { - console.error("Custom prompt found but no end delimiter", line) - } - const prompt = line.slice(0, promptStart).replace(customPromptLiteralStart, "") - let value = line.slice(promptStart + promptLiteralStart.length); - useLines.push({ - type: "input", - value: value, - prompt: prompt - }); - } else { - buffer.push(line); - } - } - saveBuffer(); - const div = document.createElement("div"); - node.replaceWith(div); - const termynal = new Termynal(div, { - lineData: useLines, - noInit: true, - lineDelay: 500 - }); - termynals.push(termynal); - }); -} - -function loadVisibleTermynals() { - termynals = termynals.filter(termynal => { - if (termynal.container.getBoundingClientRect().top - innerHeight <= 0) { - termynal.init(); - return false; - } - return true; - }); -} -window.addEventListener("scroll", loadVisibleTermynals); -createTermynals(); -loadVisibleTermynals(); diff --git a/workshop/content/docs/assets/javascripts/termynal.js b/workshop/content/docs/assets/javascripts/termynal.js deleted file mode 100644 index a2e756d..0000000 --- a/workshop/content/docs/assets/javascripts/termynal.js +++ /dev/null @@ -1,264 +0,0 @@ -/** - * termynal.js - * A lightweight, modern and extensible animated terminal window, using - * async/await. - * - * @author Ines Montani - * @version 0.0.1 - * @license MIT - */ - - 'use strict'; - - /** Generate a terminal widget. */ - class Termynal { - /** - * Construct the widget's settings. - * @param {(string|Node)=} container - Query selector or container element. - * @param {Object=} options - Custom settings. - * @param {string} options.prefix - Prefix to use for data attributes. - * @param {number} options.startDelay - Delay before animation, in ms. - * @param {number} options.typeDelay - Delay between each typed character, in ms. - * @param {number} options.lineDelay - Delay between each line, in ms. - * @param {number} options.progressLength - Number of characters displayed as progress bar. - * @param {string} options.progressChar – Character to use for progress bar, defaults to █. - * @param {number} options.progressPercent - Max percent of progress. - * @param {string} options.cursor – Character to use for cursor, defaults to ▋. - * @param {Object[]} lineData - Dynamically loaded line data objects. - * @param {boolean} options.noInit - Don't initialise the animation. - */ - constructor(container = '#termynal', options = {}) { - this.container = (typeof container === 'string') ? document.querySelector(container) : container; - this.pfx = `data-${options.prefix || 'ty'}`; - this.originalStartDelay = this.startDelay = options.startDelay - || parseFloat(this.container.getAttribute(`${this.pfx}-startDelay`)) || 600; - this.originalTypeDelay = this.typeDelay = options.typeDelay - || parseFloat(this.container.getAttribute(`${this.pfx}-typeDelay`)) || 90; - this.originalLineDelay = this.lineDelay = options.lineDelay - || parseFloat(this.container.getAttribute(`${this.pfx}-lineDelay`)) || 1500; - this.progressLength = options.progressLength - || parseFloat(this.container.getAttribute(`${this.pfx}-progressLength`)) || 40; - this.progressChar = options.progressChar - || this.container.getAttribute(`${this.pfx}-progressChar`) || '█'; - this.progressPercent = options.progressPercent - || parseFloat(this.container.getAttribute(`${this.pfx}-progressPercent`)) || 100; - this.cursor = options.cursor - || this.container.getAttribute(`${this.pfx}-cursor`) || '▋'; - this.lineData = this.lineDataToElements(options.lineData || []); - this.loadLines() - if (!options.noInit) this.init() - } - - loadLines() { - // Load all the lines and create the container so that the size is fixed - // Otherwise it would be changing and the user viewport would be constantly - // moving as she/he scrolls - const finish = this.generateFinish() - finish.style.visibility = 'hidden' - this.container.appendChild(finish) - // Appends dynamically loaded lines to existing line elements. - this.lines = [...this.container.querySelectorAll(`[${this.pfx}]`)].concat(this.lineData); - for (let line of this.lines) { - line.style.visibility = 'hidden' - this.container.appendChild(line) - } - const restart = this.generateRestart() - restart.style.visibility = 'hidden' - this.container.appendChild(restart) - this.container.setAttribute('data-termynal', ''); - } - - /** - * Initialise the widget, get lines, clear container and start animation. - */ - init() { - /** - * Calculates width and height of Termynal container. - * If container is empty and lines are dynamically loaded, defaults to browser `auto` or CSS. - */ - const containerStyle = getComputedStyle(this.container); - this.container.style.width = containerStyle.width !== '0px' ? - containerStyle.width : undefined; - this.container.style.minHeight = containerStyle.height !== '0px' ? - containerStyle.height : undefined; - - this.container.setAttribute('data-termynal', ''); - this.container.innerHTML = ''; - for (let line of this.lines) { - line.style.visibility = 'visible' - } - this.start(); - } - - /** - * Start the animation and rener the lines depending on their data attributes. - */ - async start() { - this.addFinish() - await this._wait(this.startDelay); - - for (let line of this.lines) { - const type = line.getAttribute(this.pfx); - const delay = line.getAttribute(`${this.pfx}-delay`) || this.lineDelay; - - if (type == 'input') { - line.setAttribute(`${this.pfx}-cursor`, this.cursor); - await this.type(line); - await this._wait(delay); - } - - else if (type == 'progress') { - await this.progress(line); - await this._wait(delay); - } - - else { - this.container.appendChild(line); - await this._wait(delay); - } - - line.removeAttribute(`${this.pfx}-cursor`); - } - this.addRestart() - this.finishElement.style.visibility = 'hidden' - this.lineDelay = this.originalLineDelay - this.typeDelay = this.originalTypeDelay - this.startDelay = this.originalStartDelay - } - - generateRestart() { - const restart = document.createElement('a') - restart.onclick = (e) => { - e.preventDefault() - this.container.innerHTML = '' - this.init() - } - restart.href = '#' - restart.setAttribute('data-terminal-control', '') - restart.innerHTML = "restart ↻" - return restart - } - - generateFinish() { - const finish = document.createElement('a') - finish.onclick = (e) => { - e.preventDefault() - this.lineDelay = 0 - this.typeDelay = 0 - this.startDelay = 0 - } - finish.href = '#' - finish.setAttribute('data-terminal-control', '') - finish.innerHTML = "fast →" - this.finishElement = finish - return finish - } - - addRestart() { - const restart = this.generateRestart() - this.container.appendChild(restart) - } - - addFinish() { - const finish = this.generateFinish() - this.container.appendChild(finish) - } - - /** - * Animate a typed line. - * @param {Node} line - The line element to render. - */ - async type(line) { - const chars = [...line.textContent]; - line.textContent = ''; - this.container.appendChild(line); - - for (let char of chars) { - const delay = line.getAttribute(`${this.pfx}-typeDelay`) || this.typeDelay; - await this._wait(delay); - line.textContent += char; - } - } - - /** - * Animate a progress bar. - * @param {Node} line - The line element to render. - */ - async progress(line) { - const progressLength = line.getAttribute(`${this.pfx}-progressLength`) - || this.progressLength; - const progressChar = line.getAttribute(`${this.pfx}-progressChar`) - || this.progressChar; - const chars = progressChar.repeat(progressLength); - const progressPercent = line.getAttribute(`${this.pfx}-progressPercent`) - || this.progressPercent; - line.textContent = ''; - this.container.appendChild(line); - - for (let i = 1; i < chars.length + 1; i++) { - await this._wait(this.typeDelay); - const percent = Math.round(i / chars.length * 100); - line.textContent = `${chars.slice(0, i)} ${percent}%`; - if (percent>progressPercent) { - break; - } - } - } - - /** - * Helper function for animation delays, called with `await`. - * @param {number} time - Timeout, in ms. - */ - _wait(time) { - return new Promise(resolve => setTimeout(resolve, time)); - } - - /** - * Converts line data objects into line elements. - * - * @param {Object[]} lineData - Dynamically loaded lines. - * @param {Object} line - Line data object. - * @returns {Element[]} - Array of line elements. - */ - lineDataToElements(lineData) { - return lineData.map(line => { - let div = document.createElement('div'); - div.innerHTML = `${line.value || ''}`; - - return div.firstElementChild; - }); - } - - /** - * Helper function for generating attributes string. - * - * @param {Object} line - Line data object. - * @returns {string} - String of attributes. - */ - _attributes(line) { - let attrs = ''; - for (let prop in line) { - // Custom add class - if (prop === 'class') { - attrs += ` class=${line[prop]} ` - continue - } - if (prop === 'type') { - attrs += `${this.pfx}="${line[prop]}" ` - } else if (prop !== 'value') { - attrs += `${this.pfx}-${prop}="${line[prop]}" ` - } - } - - return attrs; - } - } - - /** - * HTML API: If current script has container(s) specified, initialise Termynal. - */ - if (document.currentScript.hasAttribute('data-termynal-container')) { - const containers = document.currentScript.getAttribute('data-termynal-container'); - containers.split('|') - .forEach(container => new Termynal(container)) - } \ No newline at end of file diff --git a/workshop/content/docs/assets/stylesheets/custom.css b/workshop/content/docs/assets/stylesheets/custom.css index 7f70d09..0b28a4a 100644 --- a/workshop/content/docs/assets/stylesheets/custom.css +++ b/workshop/content/docs/assets/stylesheets/custom.css @@ -1,34 +1,24 @@ -.termynal-comment { - color: #4a968f; - font-style: italic; - display: block; -} - -.termy [data-termynal] { - white-space: pre-wrap; -} - -a.external-link::after { - /* \00A0 is a non-breaking space - to make the mark be on the same line as the link - */ - content: "\00A0[↪]"; -} - -a.internal-link::after { - /* \00A0 is a non-breaking space - to make the mark be on the same line as the link - */ - content: "\00A0↪"; -} - -.map { - width: 100%; - height: 300px; -} - -.map iframe { - width: 100%; - height: 100%; /* Optionally, set the height to 100% if you want the iframe to fill the container vertically as well */ - border: none; /* Optionally, remove the iframe border */ -} +a.external-link::after { + /* \00A0 is a non-breaking space + to make the mark be on the same line as the link + */ + content: "\00A0[↪]"; +} + +a.internal-link::after { + /* \00A0 is a non-breaking space + to make the mark be on the same line as the link + */ + content: "\00A0↪"; +} + +.map { + width: 100%; + height: 300px; +} + +.map iframe { + width: 100%; + height: 100%; /* Optionally, set the height to 100% if you want the iframe to fill the container vertically as well */ + border: none; /* Optionally, remove the iframe border */ +} diff --git a/workshop/content/docs/assets/stylesheets/termynal.css b/workshop/content/docs/assets/stylesheets/termynal.css deleted file mode 100644 index dfe3caa..0000000 --- a/workshop/content/docs/assets/stylesheets/termynal.css +++ /dev/null @@ -1,108 +0,0 @@ -/** - * termynal.js - * - * @author Ines Montani - * @version 0.0.1 - * @license MIT - */ - - :root { - --color-bg: #252a33; - --color-text: #eee; - --color-text-subtle: #a2a2a2; -} - -[data-termynal] { - width: 750px; - max-width: 100%; - background: var(--color-bg); - color: var(--color-text); - font-size: 18px; - /* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */ - font-family: 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; - border-radius: 4px; - padding: 75px 45px 35px; - position: relative; - -webkit-box-sizing: border-box; - box-sizing: border-box; -} - -[data-termynal]:before { - content: ''; - position: absolute; - top: 15px; - left: 15px; - display: inline-block; - width: 15px; - height: 15px; - border-radius: 50%; - /* A little hack to display the window buttons in one pseudo element. */ - background: #d9515d; - -webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; - box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; -} - -[data-termynal]:after { - content: 'bash'; - position: absolute; - color: var(--color-text-subtle); - top: 5px; - left: 0; - width: 100%; - text-align: center; -} - -a[data-terminal-control] { - text-align: right; - display: block; - color: #aebbff; -} - -[data-ty] { - display: block; - line-height: 2; -} - -[data-ty]:before { - /* Set up defaults and ensure empty lines are displayed. */ - content: ''; - display: inline-block; - vertical-align: middle; -} - -[data-ty="input"]:before, -[data-ty-prompt]:before { - margin-right: 0.75em; - color: var(--color-text-subtle); -} - -[data-ty="input"]:before { - content: '$'; -} - -[data-ty][data-ty-prompt]:before { - content: attr(data-ty-prompt); -} - -[data-ty-cursor]:after { - content: attr(data-ty-cursor); - font-family: monospace; - margin-left: 0.5em; - -webkit-animation: blink 1s infinite; - animation: blink 1s infinite; -} - - -/* Cursor animation */ - -@-webkit-keyframes blink { - 50% { - opacity: 0; - } -} - -@keyframes blink { - 50% { - opacity: 0; - } -} diff --git a/workshop/content/mkdocs.yml b/workshop/content/mkdocs.yml deleted file mode 100644 index 1317cf8..0000000 --- a/workshop/content/mkdocs.yml +++ /dev/null @@ -1,92 +0,0 @@ -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" -site_url: https://mapserver.github.io/getting-started-with-mapserver -repo_url: https://github.com/mapserver/getting-started-with-mapserver -nav: - - Home: index.md - - Workshop Environment Setup: setup.md - - Introduction: - - Workshop Overview: introduction/introduction.md - - Docker: introduction/docker.md - - MapServer: introduction/mapserver.md - - Command Line: introduction/commandline.md - - OpenLayers: introduction/openlayers.md - - Mapfile: - - Lines: mapfile/lines.md - - Points: mapfile/points.md - - Labels: mapfile/labels.md - - Polygons: mapfile/polygons.md - - Inputs: - - Raster Data: inputs/raster.md - - Vector Data: inputs/vector.md - - Databases: inputs/databases.md - - Outputs: - - WMS: outputs/wms.md - - WFS: outputs/wfs.md - - WCS: outputs/wcs.md - - Tiles: outputs/tiles.md - - Vector Tiles: outputs/vector-tiles.md - - OGC API - Features: outputs/ogcapi-features.md - - Advanced: - - ArcGIS Feature Server: advanced/arcgis.md - - GDAL Pipeline: advanced/gdalg.md - - Vector Symbols: advanced/symbols.md - - Clusters: advanced/clusters.md - - SLD: advanced/sld.md - - STAC: advanced/stac.md - - Non-EPSG CRSs: advanced/other-projections.md - # - Apache: advanced/apache.md - # - MapScript: advanced/mapscript.md - - Summary: summary.md - - Credits and Thanks: credits.md - -use_directory_urls: true - -theme: - name: material - palette: - # See https://squidfunk.github.io/mkdocs-material/setup/changing-the-colors/#color-scheme - # Default is indigo (blue) - scheme: mapserver - features: - - navigation.indexes - - content.code.copy - - content.code.select - favicon: assets/favicon.ico - logo: assets/images/mapserver-logo.png - -plugins: - - search - -markdown_extensions: - - meta # option to add some meta tags on top, title, author, date, etc - - admonition # adds the note, question, tip boxes, eg: !!! tip "my tip" - - pymdownx.details # advanced collapsible panels - - pymdownx.superfences # advanced features; such as line number, flow chart, python shell - - pymdownx.tabbed: - alternate_style: true - - pymdownx.highlight: - anchor_linenums: true - line_spans: __span - pygments_lang_class: true - - pymdownx.inlinehilite - # https://squidfunk.github.io/mkdocs-material/reference/code-blocks/#embedding-external-files - - pymdownx.snippets: - check_paths: true - base_path: ["../../workshop/exercises/mapfiles", "../../workshop/exercises/app/js", "../../workshop/exercises/app", "./docs"] - - footnotes # notes bottom of page - - attr_list # used to size images - - md_in_html # used to size images - -extra_css: - # mapserver primary color with light and dark variations from material.io - # https://material.io/resources/color/#!/?view.left=0&view.right=1 - - assets/stylesheets/mapserver.css - - assets/stylesheets/termynal.css - - assets/stylesheets/custom.css - -extra_javascript: - - assets/javascripts/termynal.js - - assets/javascripts/custom.js diff --git a/workshop/content/requirements.txt b/workshop/content/requirements.txt index 21f876b..4a24c7c 100644 --- a/workshop/content/requirements.txt +++ b/workshop/content/requirements.txt @@ -1,2 +1 @@ -mkdocs -mkdocs-material \ No newline at end of file +zensical \ No newline at end of file diff --git a/workshop/content/zensical.toml b/workshop/content/zensical.toml new file mode 100644 index 0000000..f98ba0e --- /dev/null +++ b/workshop/content/zensical.toml @@ -0,0 +1,98 @@ +[project] +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" +site_url = "https://mapserver.github.io/getting-started-with-mapserver" +repo_url = "https://github.com/mapserver/getting-started-with-mapserver" +docs_dir = "docs" +directory_urls = true +extra_css = [ + "assets/stylesheets/mapserver.css", + "assets/stylesheets/custom.css" +] +extra_javascript = [ + "assets/javascripts/custom.js" +] + +nav = [ + { "Home" = "index.md" }, + { "Workshop Environment Setup" = "setup.md" }, + { "Introduction" = [ + { "Workshop Overview" = "introduction/introduction.md" }, + { "Docker" = "introduction/docker.md" }, + { "MapServer" = "introduction/mapserver.md" }, + { "Command Line" = "introduction/commandline.md" }, + { "OpenLayers" = "introduction/openlayers.md" } + ]}, + { "Mapfile" = [ + { "Lines" = "mapfile/lines.md" }, + { "Points" = "mapfile/points.md" }, + { "Labels" = "mapfile/labels.md" }, + { "Polygons" = "mapfile/polygons.md" } + ]}, + { "Inputs" = [ + { "Raster Data" = "inputs/raster.md" }, + { "Vector Data" = "inputs/vector.md" }, + { "Databases" = "inputs/databases.md" } + ]}, + { "Outputs" = [ + { "WMS" = "outputs/wms.md" }, + { "WFS" = "outputs/wfs.md" }, + { "WCS" = "outputs/wcs.md" }, + { "Tiles" = "outputs/tiles.md" }, + { "Vector Tiles" = "outputs/vector-tiles.md" }, + { "OGC API - Features" = "outputs/ogcapi-features.md" } + ]}, + { "Advanced" = [ + { "ArcGIS Feature Server" = "advanced/arcgis.md" }, + { "GDAL Pipeline" = "advanced/gdalg.md" }, + { "Vector Symbols" = "advanced/symbols.md" }, + { "Clusters" = "advanced/clusters.md" }, + { "SLD" = "advanced/sld.md" }, + { "STAC" = "advanced/stac.md" }, + { "Non-EPSG CRSs" = "advanced/other-projections.md" } + ]}, + { "Summary" = "summary.md" }, + { "Credits and Thanks" = "credits.md" } +] + +[project.theme] +language = "en" +variant = "material" +scheme = "mapserver" +favicon = "assets/favicon.ico" +logo = "assets/images/mapserver-logo.png" +features = [ + "navigation.indexes", + "content.code.copy", + "content.code.select" +] + +# see https://github.com/zensical/zensical/blob/master/python/zensical/bootstrap/zensical.toml +# for examples + +[project.markdown_extensions.meta] +[project.markdown_extensions.admonition] +[project.markdown_extensions.footnotes] +[project.markdown_extensions.attr_list] +[project.markdown_extensions.md_in_html] + +[project.markdown_extensions.pymdownx.details] +[project.markdown_extensions.pymdownx.superfences] +[project.markdown_extensions.pymdownx.inlinehilite] + +[project.markdown_extensions.pymdownx.tabbed] +alternate_style = true + +[project.markdown_extensions.pymdownx.highlight] +anchor_linenums = true +line_spans = "__span" +pygments_lang_class = true + +[project.markdown_extensions.pymdownx.snippets] +check_paths = true +base_path = ["../../workshop/exercises/mapfiles", "../../workshop/exercises/app/js", "../../workshop/exercises/app", "./docs"] + +[project.extra] +homepage = "https://mapserver.github.io/getting-started-with-mapserver" \ No newline at end of file