diff --git a/.gitignore b/.gitignore index 5f240c4f..19e604d9 100644 --- a/.gitignore +++ b/.gitignore @@ -66,3 +66,7 @@ typings/ # configs, if generated transpiled-configs/ + +#demo html pages, if generated +demo/* +index.html diff --git a/demo/client-filter.html b/demo/client-filter.html deleted file mode 100644 index 149f320e..00000000 --- a/demo/client-filter.html +++ /dev/null @@ -1,155 +0,0 @@ - - - - - Tilegarden - - - - - - - - - - - - -
- -
-

Filter by Query

-

Allow users to modify pre-defined layers by specifying filter conditions.

-
-
- https://yourtiles.cloudfront.net/tile/{z}/{x}/{y}.png?layers=PARK,PRIV -
-
-
-
-
- PWD stormwater billing parcels. The primary purpose of PWD_PARCEL layer is to calculate parcel-based stormwater charges for PWD customers under the new parcel-based stormwater billing program. | City of Philadelphia -
-
-
-
-
- - -
-
-
-
-
-
- - - - - diff --git a/demo/layer-filter.html b/demo/layer-filter.html deleted file mode 100644 index ed31eee5..00000000 --- a/demo/layer-filter.html +++ /dev/null @@ -1,145 +0,0 @@ - - - - - Tilegarden - - - - - - - - - - - - -
- -
-

Filter by Layer

-

Define customizable map layers that can be queried by your users.

-
-
- https://yourtiles.cloudfront.net/tile/{z}/{x}/{y}.png?layers=["PARK","PRIV"] -
-
-
-
-
- This point layer contains all the wastewater and stormwater inlets in Philadelphia with latitude and longitude coordinates. | City of Philadelphia -
-
-
-
-
- - -
- -
-
-
-
- - - - - diff --git a/demo/raster.html b/demo/raster.html deleted file mode 100644 index d8a164da..00000000 --- a/demo/raster.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - Tilegarden | Raster Tiles - - - - - - - - - - - - -
- -
-

Raster Tiles

-

- Serve your geospatial data as raster tiles, specified and styled via CartoCSS. -

-
-
- https://yourtiles.cloudfront.net/tile/{z}/{x}/{y}.png -
-
-
-
-
- This point layer contains all the wastewater and stormwater inlets in Philadelphia with latitude and longitude coordinates. | City of Philadelphia -
-
-
-
-
- - -
-
-
-
-
- - - - diff --git a/demo/utf-grid.html b/demo/utf-grid.html deleted file mode 100644 index 500c02a8..00000000 --- a/demo/utf-grid.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - Tilegarden | UTF Grids - - - - - - - - - - - - - -
- -
-

UTF Grids

-

Serve UTF grids and interact with your data.

-
-
- https://yourtiles.cloudfront.net/tile/{z}/{x}/{y}.png?utfFields=owner,operator -
-
-
- -
Mouse over me!
- -
- This point layer contains all the wastewater and stormwater inlets in Philadelphia with latitude and longitude coordinates. | City of Philadelphia -
-
-
- -
-
- - - - - diff --git a/demo/vector.html b/demo/vector.html deleted file mode 100644 index 906a3ec8..00000000 --- a/demo/vector.html +++ /dev/null @@ -1,207 +0,0 @@ - - - - - Tilegarden | Vector Tiles - - - - - - - - - - - - - -
- -
-

Vector Tiles

-

Serve your geospatial data as Mapbox Vector Tiles for client-side styling and manipulation.

-
-
- https://yourtiles.cloudfront.net/vector/{z}/{x}/{y}.png -
-
-
-
-
- This point layer contains all the wastewater and stormwater inlets in Philadelphia with latitude and longitude coordinates. | City of Philadelphia -
-
-
-
-
- - -
-
- Style - -
-
- -
-
-
-
-
- - - - - diff --git a/docker-compose.yml b/docker-compose.yml index 408df4b9..93811b4f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,6 +44,14 @@ services: - ./src/terraform:/home/terraform - $HOME/.aws:/root/.aws:ro + pug: + build: + context: ./src/pug + dockerfile: Dockerfile + volumes: + - ./src/pug:/home/pug + - ./src/pug/out:/home/pug/out + volumes: node_modules: bin: diff --git a/index.html b/index.html deleted file mode 100644 index c525f08d..00000000 --- a/index.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - - Tilegarden - - - - - - - - - - -
-
- -
-
-
-
-

Tilegarden: Serverless Mapnik Rendering

- -

- Tilegarden is a serverless tileserver built for AWS Lambda. All the power of a conventional tileserver without needing to worry about provisioning, scaling, or uptime costs. - - Click below for demos of Tilegarden's functionality. -

-
-

Demos:

- -
-
-
- - - - - diff --git a/scripts/build-demo-pages b/scripts/build-demo-pages new file mode 100755 index 00000000..f50b1b10 --- /dev/null +++ b/scripts/build-demo-pages @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +docker-compose run pug +cp -R src/pug/out/* . +cp -R src/pug/pages/demo/scripts/ demo/ diff --git a/scripts/deploy-demo.sh b/scripts/deploy-demo.sh index 62a0b86f..6f52b5c4 100755 --- a/scripts/deploy-demo.sh +++ b/scripts/deploy-demo.sh @@ -15,6 +15,13 @@ cd "${REPO_TEMP}" git checkout gh-pages echo "Merging with ${CURRENT_BRANCH}" git merge "${CURRENT_BRANCH}" + +# Generate new github pages +./scripts/update +./scripts/build-demo-pages +git add -A +git commit -m "Re-build html pages" + # make sure there wasn't a merge conflict # exit if there's a merge conflict, this can be handled # normally, but the actual tests haven't failed @@ -23,6 +30,7 @@ if [[ $(git ls-files -u) ]]; then exit 0 fi + # Decrypt .env openssl aes-256-cbc -K $encrypted_f456ba71c182_key -iv $encrypted_f456ba71c182_iv -in .env.enc -out .env -d diff --git a/src/pug/.gitignore b/src/pug/.gitignore new file mode 100644 index 00000000..2d19fc76 --- /dev/null +++ b/src/pug/.gitignore @@ -0,0 +1 @@ +*.html diff --git a/src/pug/Dockerfile b/src/pug/Dockerfile new file mode 100644 index 00000000..b1004556 --- /dev/null +++ b/src/pug/Dockerfile @@ -0,0 +1,8 @@ +FROM node + +RUN yarn global add pug-cli + +COPY . /home/pug +WORKDIR /home/pug + +ENTRYPOINT ["pug", "pages", "-o", "out/", "-P"] diff --git a/src/pug/components/footer.pug b/src/pug/components/footer.pug new file mode 100644 index 00000000..d4806158 --- /dev/null +++ b/src/pug/components/footer.pug @@ -0,0 +1,6 @@ +footer + .container.text-center + span(class='.text-muted') + | © 2018 Azavea. Built for the + | + a(href='https://fellowship.azavea.com/') 2018 Azavea Open Source Fellowship. diff --git a/src/pug/components/header.pug b/src/pug/components/header.pug new file mode 100644 index 00000000..054e2489 --- /dev/null +++ b/src/pug/components/header.pug @@ -0,0 +1,62 @@ +- + var pageList = [ + { + path: 'raster.html', + title: 'Raster Tiles' + }, + { + path: 'vector.html', + title: 'Vector Tiles' + }, + { + path: 'layer-filter.html', + title: 'Queryable Layers' + }, + { + path: 'client-filter.html', + title: 'Client-Side Filtering' + }, + { + path: 'utf-grid.html', + title: 'UTF Grids' + } + ] +- var onHomepage = pageTitle === 'Home' +- var relPath = onHomepage ? 'demo/' : '' + +mixin homeLink() + if onHomepage + li.nav-item.active + a.nav-link(href='#') Home + else + li.nav-item + a.nav-link(href='../index.html') Home + +mixin dropdownItem(page) + - var path = pageTitle === page.name ? '#' : relPath + page.path + a.dropdown-item(href=path)= page.title + +header + .container + nav.navbar.navbar-expand-sm.navbar-light.bg-light + button.navbar-toggler(type='button' data-toggle='collapse' data-target='#navbar') + span.navbar-toggler-icon + + .collapse.navbar-collapse#navbar + ul.navbar-nav.mr-auto + +homeLink() + li.nav-item.dropdown + - var dropdownClasses = "nav-link dropdown-toggle" + (onHomepage ? '' : ' active') + a#demoDropdown(href='#' role='button' data-toggle='dropdown' class=dropdownClasses) Demo + .dropdown-menu + each page in pageList + +dropdownItem(page) + li.nav-item + a.nav-link(href='https://github.com/azavea/tilegarden/blob/develop/README.md') Documentation + a.navbar-brand(href='https://github.com/azavea/tilegarden/') View on GitHub + a.github-button( + href='https://github.com/azavea/tilegarden/archive/master.zip' + data-icon='octicon-cloud-download' + data-size='large' + aria-label='Download azavea/tilegarden on GitHub' + ) Download diff --git a/src/pug/components/layout.pug b/src/pug/components/layout.pug new file mode 100644 index 00000000..2ca3fae8 --- /dev/null +++ b/src/pug/components/layout.pug @@ -0,0 +1,45 @@ +doctype html +html + block vars + head + title Tilegarden | #{pageTitle} + // Bootstrap content + link( + rel='stylesheet' + href='https://stackpath.bootstrapcdn.com/bootstrap/4.1.2/css/bootstrap.min.css' + integrity='sha384-Smlep5jCw/wG7hdkwQ/Z5nLIefveQRIY9nfy6xoR1uRYBtpZgI6339F5dgvm/e9B' + crossorigin='anonymous' + ) + script( + src='https://code.jquery.com/jquery-3.3.1.slim.min.js' + integrity='sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo' + crossorigin='anonymous' + ) + script( + src='https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js' + integrity='sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49' + crossorigin='anonymous' + ) + script( + src='https://stackpath.bootstrapcdn.com/bootstrap/4.1.2/js/bootstrap.min.js' + integrity='sha384-o+RDsa0aLu++PJvFqy8fFScvbHFLtbvScb8AjopnFD+iEQ7wo/CG0xlczd+2O/em' + crossorigin='anonymous' + ) + + // Github button + script( + async + defer + src='https://buttons.github.io/buttons.js' + ) + + // Custom stylesheet + include ../components/styles.pug + + block scripts + body + include ../components/header.pug + .container.content + .mt-3 + block content + include ../components/footer.pug diff --git a/src/pug/components/leaflet-scripts.pug b/src/pug/components/leaflet-scripts.pug new file mode 100644 index 00000000..1d06fa83 --- /dev/null +++ b/src/pug/components/leaflet-scripts.pug @@ -0,0 +1,7 @@ +// Leaflet scripts +link( + rel='stylesheet' href='https://unpkg.com/leaflet@1.3.2/dist/leaflet.css' +) +script( + src='https://unpkg.com/leaflet@1.3.2/dist/leaflet.js' +) diff --git a/src/pug/components/mapbox-scripts.pug b/src/pug/components/mapbox-scripts.pug new file mode 100644 index 00000000..ef3380cd --- /dev/null +++ b/src/pug/components/mapbox-scripts.pug @@ -0,0 +1,8 @@ +// MapboxGL +link( + href='https://api.mapbox.com/mapbox-gl-js/v0.47.0/mapbox-gl.css' rel='stylesheet' +) +script( + src='https://api.mapbox.com/mapbox-gl-js/v0.47.0/mapbox-gl.js' +) + diff --git a/demo/style.css b/src/pug/components/styles.pug similarity index 59% rename from demo/style.css rename to src/pug/components/styles.pug index b0fb1637..902b3aaa 100644 --- a/demo/style.css +++ b/src/pug/components/styles.pug @@ -1,27 +1,28 @@ -html, body { +style(type='text/css'). + html, body { height: 100%; -} + } -body { + body { display: flex; flex-direction: column; -} + } -footer { + footer { width: 100%; line-height: 40px; flex-shrink: 0; -} + } -footer div { + footer div { background-color: #f8f9fa; -} + } -.content { + .content { flex: 1 0 auto; -} + } -#map { + #map { width: 100%; height: 500px; -} + } diff --git a/src/pug/pages/demo/client-filter.pug b/src/pug/pages/demo/client-filter.pug new file mode 100644 index 00000000..8f0aceb3 --- /dev/null +++ b/src/pug/pages/demo/client-filter.pug @@ -0,0 +1,29 @@ +extends ../../components/layout.pug + +block vars + - var pageTitle = 'Client-Side Filtering' + +block scripts + include ../../components/leaflet-scripts.pug + script(src='./scripts/client-filter.js') + +block content + h2.display-4 Filter by Query + p.lead Allow users to modify pre-defined layers by specifying filter conditions. + .row + .m-auto + code#urlExample https://yourtiles.cloudfront.net/tile/{z}/{x}/{y}.png?layers=["PARK","PRIV"] + .col-8 + figure.figure + .figure-img#map + figcaption.figure-caption#dataDesc + | This point layer contains all the wastewater and stormwater inlets in Philadelphia with latitude and longitude coordinates. | + | + a(href="https://www.opendataphilly.org/dataset/water-inlets") City of Philadelphia + .col + .form-group + label(for='querybox') Query Schema + textarea.form-control#querybox(rows="14") + .w-100.text-right + button.btn.btn-primary.mt-1#btnUpdateQuery Set query + diff --git a/src/pug/pages/demo/layer-filter.pug b/src/pug/pages/demo/layer-filter.pug new file mode 100644 index 00000000..b39eb4c3 --- /dev/null +++ b/src/pug/pages/demo/layer-filter.pug @@ -0,0 +1,48 @@ +extends ../../components/layout.pug + +mixin selectorOption(option, selected) + option(selected=selected)= option + +block vars + - var pageTitle = 'Queryable Layers' + - + var options = [ + 'AIRPRT', + 'CHEL', + 'FEDERAL', + 'LM', + 'PARK', + 'PRIV', + 'PRIVPWDMAINT', + 'PWD', + 'STATE', + 'USNAVY' + ] + +block scripts + include ../../components/leaflet-scripts.pug + script(src='./scripts/layer-filter.js') + +block content + h2.display-4 Filter by Layer + p.lead Define customizable map layers that can be queried by your users. + .row + .m-auto + code#urlExample https://yourtiles.cloudfront.net/tile/{z}/{x}/{y}.png?layers=["PARK","PRIV"] + .col-8 + figure.figure + .figure-img#map + figcaption.figure-caption#dataDesc + | This point layer contains all the wastewater and stormwater inlets in Philadelphia with latitude and longitude coordinates. | + | + a(href="https://www.opendataphilly.org/dataset/water-inlets") City of Philadelphia + .col + .form-group.h-75 + label(for='selector') Filter (by inlet owner): + select.custom-select.h-75#selector(multiple) + each option in options + if option === 'PARK' || option === 'PRIV' + +selectorOption(option, true) + else + +selectorOption(option, false) + diff --git a/src/pug/pages/demo/raster.pug b/src/pug/pages/demo/raster.pug new file mode 100644 index 00000000..edfa47f6 --- /dev/null +++ b/src/pug/pages/demo/raster.pug @@ -0,0 +1,51 @@ +extends ../../components/layout.pug + +mixin selectorOption(option) + option(value=option.value)= option.text + +block vars + - var pageTitle = 'Raster Tiles' + - + var options = [ + { + value: 'inlets', + text: 'Points' + }, + { + value: 'street_centerline', + text: 'Lines' + }, + { + value: 'pwd_parcels', + text: 'Polygons' + } + ] + +block scripts + include ../../components/leaflet-scripts.pug + script(src='./scripts/raster.js') + +block content + h2.display-4 Raster Tiles + p.lead + | Serve your geospatial data as raster tiles, specified and styled via + | + a(href='https://cartocss.readthedocs.io/en/latest/index.html') CartoCSS + | . + .row + .m-auto + code#urlExample https://yourtiles.cloudfront.net/tile/{z}/{x}/{y}.png + .col-8 + figure.figure + .figure-img#map + figcaption.figure-caption#dataDesc + | This point layer contains all the wastewater and stormwater inlets in Philadelphia with latitude and longitude coordinates. | + | + a(href="https://www.opendataphilly.org/dataset/water-inlets") City of Philadelphia + .col + .form-group + label(for='selector') Data set: + select.custom-select#selector + each option in options + +selectorOption(option) + diff --git a/demo/L.UTFGrid-min.js b/src/pug/pages/demo/scripts/L.UTFGrid-min.js similarity index 100% rename from demo/L.UTFGrid-min.js rename to src/pug/pages/demo/scripts/L.UTFGrid-min.js diff --git a/src/pug/pages/demo/scripts/client-filter.js b/src/pug/pages/demo/scripts/client-filter.js new file mode 100644 index 00000000..2abecac2 --- /dev/null +++ b/src/pug/pages/demo/scripts/client-filter.js @@ -0,0 +1,70 @@ +"use-strict" + +document.addEventListener('DOMContentLoaded', function pageLoaded() { + var map = L.map('map', { + center: [39.97, -75.15], zoom: 11 + }); + + // add basemap + var basemap = L.tileLayer('https://korona.geog.uni-heidelberg.de/tiles/roadsg/x={x}&y={y}&z={z}', { + maxZoom: 19, + attribution: 'Imagery from GIScience Research Group @ University of Heidelberg — Map data © OpenStreetMap' + }); + map.addLayer(basemap) + + // dynamically set path based on host + var PROD_PATH = 'https://d3hvvzen5hf4hb.cloudfront.net/'; + var DEV_PATH = 'http://localhost:3000/'; + var TILESERVER_PATH; + var host = window.location.hostname; + if (!host || host === 'localhost') TILESERVER_PATH = DEV_PATH; + else TILESERVER_PATH = PROD_PATH; + + // add filtered layers + function newLayer (filters) { + return L.tileLayer(TILESERVER_PATH + 'tile/{z}/{x}/{y}.png?config=data-type-example&layers=' + encodeURIComponent(JSON.stringify(filters))) + } + + var EXAMPLE_PATH = 'https://yourtiles.cloudfront.net/tile/{z}/{x}/{y}.png?layers=' + + var INITIAL_QUERY = [ + { + name: 'pwd_parcels', + mode: 'AND', + filters: [ + { + col: 'owner2', + val: 'OF PHILADELPHIA', + }, + { + col: 'gross_area', + op: '>', + val: '3000' + } + ] + } + ] + + // keep track of active layers so + var currentSelection; + document.getElementById("btnUpdateQuery").addEventListener('click', function selectorMouseUp() { + // remove old layer + map.removeLayer(currentSelection); + + // get new layer schema + var layerObj; + try { + layerObj = JSON.parse(document.getElementById('querybox').value); + currentSelection = newLayer(layerObj); + currentSelection.addTo(map); + document.getElementById('urlExample').innerText = EXAMPLE_PATH + JSON.stringify(layerObj); + } catch (e) { + console.error(e); + } + }); + + currentSelection = newLayer(INITIAL_QUERY); + document.getElementById('urlExample').innerText = EXAMPLE_PATH + JSON.stringify(INITIAL_QUERY); + document.getElementById('querybox').value = JSON.stringify(INITIAL_QUERY, null, 2); + map.addLayer(currentSelection); +}); diff --git a/src/pug/pages/demo/scripts/layer-filter.js b/src/pug/pages/demo/scripts/layer-filter.js new file mode 100644 index 00000000..00f140f9 --- /dev/null +++ b/src/pug/pages/demo/scripts/layer-filter.js @@ -0,0 +1,49 @@ +"use-strict" + +document.addEventListener('DOMContentLoaded', function pageLoaded() { + var map = L.map('map', { + center: [39.99, -75.15], zoom: 11 + }); + + // add basemap + var basemap = L.tileLayer('https://korona.geog.uni-heidelberg.de/tiles/roadsg/x={x}&y={y}&z={z}', { + maxZoom: 19, + attribution: 'Imagery from GIScience Research Group @ University of Heidelberg — Map data © OpenStreetMap' + }); + map.addLayer(basemap) + + // dynamically set path based on host + var PROD_PATH = 'https://d3hvvzen5hf4hb.cloudfront.net/'; + var DEV_PATH = 'http://localhost:3000/'; + var TILESERVER_PATH; + var host = window.location.hostname; + if (!host || host === 'localhost') TILESERVER_PATH = DEV_PATH; + else TILESERVER_PATH = PROD_PATH; + + // add filtered layers + function newLayer (filters) { + return L.tileLayer(TILESERVER_PATH + 'tile/{z}/{x}/{y}.png?config=point-example&layers=' + filters) + } + + var EXAMPLE_PATH = 'https://yourtiles.cloudfront.net/tile/{z}/{x}/{y}.png?layers=' + + // keep track of active layers so + var currentSelection; + document.getElementById("selector").addEventListener('mouseup', function selectorMouseUp() { + // remove old layer + map.removeLayer(currentSelection); + + // get new layer names from selector + var layerNames = Object.values(this.selectedOptions).map(function(v) { + return '"' + v.value + '"'; + }).join(','); + layerNames = "[" + layerNames + "]" + + currentSelection = newLayer(layerNames); + currentSelection.addTo(map); + document.getElementById('urlExample').innerText = EXAMPLE_PATH + layerNames; + }); + + currentSelection = newLayer('["PRIV","PARK"]'); + map.addLayer(currentSelection); +}); diff --git a/src/pug/pages/demo/scripts/raster.js b/src/pug/pages/demo/scripts/raster.js new file mode 100644 index 00000000..47f946fe --- /dev/null +++ b/src/pug/pages/demo/scripts/raster.js @@ -0,0 +1,47 @@ +"use-strict" + +document.addEventListener('DOMContentLoaded', function pageLoaded() { + var map = L.map('map', { + center: [39.96,-75.15,], + zoom: 15, + minZoom: 13, + }); + + // add basemap + var basemap = L.tileLayer('https://korona.geog.uni-heidelberg.de/tiles/roadsg/x={x}&y={y}&z={z}', { + maxZoom: 19, + attribution: 'Imagery from GIScience Research Group @ University of Heidelberg — Map data © OpenStreetMap' + }); + map.addLayer(basemap); + + var LAYER_DESCS = { + 'inlets': 'This point layer contains all the wastewater and stormwater inlets in Philadelphia with latitude and longitude coordinates. | City of Philadelphia', + 'street_centerline': 'Street centerlines. Used citywide as base layer for many purposes/applications. The street centerline is available for reference purposes only and does not represent exact engineering specifiactions. | City of Philadelphia', + 'pwd_parcels': 'PWD stormwater billing parcels. The primary purpose of PWD_PARCEL layer is to calculate parcel-based stormwater charges for PWD customers under the new parcel-based stormwater billing program. | City of Philadelphia', + } + + // dynamically set path based on host + var PROD_PATH = 'https://d3hvvzen5hf4hb.cloudfront.net/'; + var DEV_PATH = 'http://localhost:3000/'; + var TILESERVER_PATH; + var host = window.location.hostname; + if (!host || host === 'localhost') TILESERVER_PATH = DEV_PATH; + else TILESERVER_PATH = PROD_PATH; + + function makeLayer (layer) { + return L.tileLayer(TILESERVER_PATH + 'tile/{z}/{x}/{y}.png?config=data-type-example&layers=["' + layer + '"]'); + } + + var currentLayer; + document.getElementById("selector").addEventListener('change', function selectorChange (e) { + map.removeLayer(currentLayer); + currentLayer = makeLayer(this.value); + map.addLayer(currentLayer); + + document.getElementById('dataDesc').innerHTML = LAYER_DESCS[this.value]; + }) + + currentLayer = makeLayer('inlets'); + map.addLayer(currentLayer); + document.getElementById('dataDesc').innerHTML = LAYER_DESCS['inlets']; +}); diff --git a/src/pug/pages/demo/scripts/utf-grid.js b/src/pug/pages/demo/scripts/utf-grid.js new file mode 100644 index 00000000..16fe53d2 --- /dev/null +++ b/src/pug/pages/demo/scripts/utf-grid.js @@ -0,0 +1,44 @@ +"use-strict" + +document.addEventListener('DOMContentLoaded', function pageLoaded() { + var map = L.map('map', { + center: [39.96, -75.15], + zoom: 15, + }); + + // add basemap + var basemap = L.tileLayer('https://korona.geog.uni-heidelberg.de/tiles/roadsg/x={x}&y={y}&z={z}', { + maxZoom: 19, + attribution: 'Imagery from GIScience Research Group @ University of Heidelberg — Map data © OpenStreetMap' + }); + map.addLayer(basemap); + + // dynamically set path based on host + var PROD_PATH = 'https://d3hvvzen5hf4hb.cloudfront.net/'; + var DEV_PATH = 'http://localhost:3000/'; + var TILESERVER_PATH; + var host = window.location.hostname; + if (!host || host === 'localhost') TILESERVER_PATH = DEV_PATH; + else TILESERVER_PATH = PROD_PATH; + + L.tileLayer(TILESERVER_PATH + 'tile/{z}/{x}/{y}.png?config=data-type-example&layers=["inlets"]').addTo(map); + var grid = L.utfGrid(TILESERVER_PATH + 'grid/{z}/{x}/{y}?config=data-type-example&layers=["inlets"]&utfFields=owner,operator', { useJsonP: false }) + + // keep track of currently active marker + //var marker + grid.on('mouseover', function gridClick(e) { + if (e.data) { + L.popup() + .setLatLng(e.latlng) + .setContent("This inlet is owned by " + e.data.owner + " and operated by " + e.data.operator + ".") + .openOn(map) + } + }) + map.addLayer(grid); + + + // prompt user to mouse over the map, remove mouseover div on mouseover + document.getElementById('mousePrompt').addEventListener('mouseover', function onMouseOver() { + this.parentNode.removeChild(this); + }) +}); diff --git a/src/pug/pages/demo/scripts/vector.js b/src/pug/pages/demo/scripts/vector.js new file mode 100644 index 00000000..79216896 --- /dev/null +++ b/src/pug/pages/demo/scripts/vector.js @@ -0,0 +1,111 @@ +"use-strict" +document.addEventListener('DOMContentLoaded', function onload() { + mapboxgl.accessToken = "pk.eyJ1IjoibWRlbHNvcmRvLWF6YXZlYSIsImEiOiJjamp5Z3M5NTcwbHZpM3ZydzAxMXF3bWY5In0.GObbyRg_IX8mONaD98SIjQ" + var map = new mapboxgl.Map({ + container: 'map', + style: 'mapbox://styles/mapbox/light-v9', + center: [ -75.15, 39.96,], + zoom: 15, + minZoom: 14, + }); + + // list of layers + var layers = { + 'pwd_parcels': { + id: 'pwd_parcels', + type: 'fill', + source: 'pwd_parcels', + 'source-layer': 'pwd_parcels', + paint: { + 'fill-color': 'orange', + 'fill-outline-color': 'red', + } + }, + 'street_centerline': { + id: 'street_centerline', + type: 'line', + source: 'street_centerline', + 'source-layer': 'street_centerline', + paint: { + 'line-color': 'red', + 'line-width': 2, + } + }, + 'inlets': { + id: 'inlets', + type: 'circle', + source: 'inlets', + 'source-layer': 'inlets', + paint: { + 'circle-color': 'orange', + 'circle-stroke-color': 'red', + 'circle-stroke-width': 1 + } + } + } + + // dynamically set path based on host + var PROD_PATH = 'https://d3hvvzen5hf4hb.cloudfront.net/'; + var DEV_PATH = 'http://localhost:3000/'; + var TILESERVER_PATH; + var host = window.location.hostname; + if (!host || host === 'localhost') TILESERVER_PATH = DEV_PATH; + else TILESERVER_PATH = PROD_PATH; + + map.on('load', function mapLoaded() { + map.addSource( + 'inlets', + { + type: 'vector', + tiles: [TILESERVER_PATH+'vector/{z}/{x}/{y}?config=data-type-example&layers=["inlets"]'] + }, + ) + + map.addSource( + 'street_centerline', + { + type: 'vector', + tiles: [TILESERVER_PATH+'vector/{z}/{x}/{y}?config=data-type-example&layers=["street_centerline"]'] + }, + ) + + map.addSource( + 'pwd_parcels', + { + type: 'vector', + tiles: [TILESERVER_PATH+'vector/{z}/{x}/{y}?config=data-type-example&layers=["pwd_parcels"]'] + }, + ) + + map.addLayer(layers['inlets']) + document.getElementById('stylebox').value = JSON.stringify(layers['inlets'].paint) + document.getElementById('dataDesc').innerHTML = LAYER_DESCS['inlets']; + }); + + var LAYER_DESCS = { + 'inlets': 'This point layer contains all the wastewater and stormwater inlets in Philadelphia with latitude and longitude coordinates. | City of Philadelphia', + 'street_centerline': 'Street centerlines. Used citywide as base layer for many purposes/applications. The street centerline is available for reference purposes only and does not represent exact engineering specifiactions. | City of Philadelphia', + 'pwd_parcels': 'PWD stormwater billing parcels. The primary purpose of PWD_PARCEL layer is to calculate parcel-based stormwater charges for PWD customers under the new parcel-based stormwater billing program. | City of Philadelphia', + } + + var currentLayer = 'inlets'; + document.getElementById("selector").addEventListener('change', function selectorChange (e) { + var selection = document.getElementById("selector").value; + console.log(JSON.stringify(selection)); + map.removeLayer(currentLayer); + currentLayer = selection; + map.addLayer(layers[currentLayer]); + document.getElementById('stylebox').value = JSON.stringify(layers[selection].paint); + document.getElementById('dataDesc').innerHTML = LAYER_DESCS[selection]; + }); + + // update styles by looping through properties of json object + document.getElementById('btnChangeStyle').addEventListener('click', function changeStyle() { + var newStyle = JSON.parse(document.getElementById('stylebox').value); + for (var p in newStyle) { + if (newStyle.hasOwnProperty(p)) { + map.setPaintProperty(currentLayer, p, newStyle[p]); + } + } + }); +}); diff --git a/src/pug/pages/demo/utf-grid.pug b/src/pug/pages/demo/utf-grid.pug new file mode 100644 index 00000000..c1f092cb --- /dev/null +++ b/src/pug/pages/demo/utf-grid.pug @@ -0,0 +1,29 @@ +extends ../../components/layout.pug + +block vars + - var pageTitle = 'UTF Grids' + +block scripts + include ../../components/leaflet-scripts.pug + script(src='./scripts/L.UTFGrid-min.js') + script(src='./scripts/utf-grid.js') + +block content + h2.display-4 UTF Grids + p.lead Serve UTF grids and interact with your data. + .row + .m-auto + code#urlExample https://yourtiles.cloudfront.net/tile/{z}/{x}/{y}.png?utfFields=owner,operator + figure.figure.w-100(style={'position': 'relative'}) + .figure-img#map(style={'position': 'absolute', 'z-index': '1'}) + .jumbotron.w-100.display-4.text-center#mousePrompt(style={ + 'position': 'absolute', + 'z-index': '2', + 'height': '500px', + 'opacity': '0.5' + }) Mouse over me! + figcaption.figure-caption#dataDesc(style={'position': 'absolute', 'top': '500px', 'z-index': '3'}) + | This point layer contains all the wastewater and stormwater inlets in Philadelphia with latitude and longitude coordinates. | + | + a(href="https://www.opendataphilly.org/dataset/water-inlets") City of Philadelphia + diff --git a/src/pug/pages/demo/vector.pug b/src/pug/pages/demo/vector.pug new file mode 100644 index 00000000..2220cf62 --- /dev/null +++ b/src/pug/pages/demo/vector.pug @@ -0,0 +1,56 @@ +extends ../../components/layout.pug + +mixin selectorOption(option) + option(value=option.value)= option.text + +block vars + - var pageTitle = 'Vector Tiles' + - + var options = [ + { + value: 'inlets', + text: 'Points' + }, + { + value: 'street_centerline', + text: 'Lines' + }, + { + value: 'pwd_parcels', + text: 'Polygons' + } + ] + +block scripts + include ../../components/mapbox-scripts.pug + script(src='./scripts/vector.js') + +block content + h2.display-4 Vector Tiles + p.lead + | Serve your geospatial data as + | + a(href=href="https://www.mapbox.com/vector-tiles/") Mapbox Vector Tiles + | + | for client-side styling and manipulation. + .row + .m-auto + code#urlExample https://yourtiles.cloudfront.net/vector/{z}/{x}/{y}.png + .col-8 + figure.figure + .figure-img#map + figcaption.figure-caption#dataDesc + | This point layer contains all the wastewater and stormwater inlets in Philadelphia with latitude and longitude coordinates. | + | + a(href="https://www.opendataphilly.org/dataset/water-inlets") City of Philadelphia + .col + .form-group + label(for='selector') Data set: + select.custom-select#selector + each option in options + +selectorOption(option) + .form-group + label(for='stylebox') Style + textarea#stylebox.form-control + div + button.btn.btn-primary#btnChangeStyle Set style diff --git a/src/pug/pages/index.pug b/src/pug/pages/index.pug new file mode 100644 index 00000000..f95075cd --- /dev/null +++ b/src/pug/pages/index.pug @@ -0,0 +1,42 @@ +extends ../components/layout.pug + +mixin demoLinks(page) + - var demoPath = 'demo/' + page.path + a.btn.btn-primary.m-1(href=demoPath type='button')= page.title + +block vars + - var pageTitle = 'Home' + - + var demoPages = [ + { + path: 'raster.html', + title: 'Serve Raster Tiles' + }, + { + path: 'vector.html', + title: 'Serve Vector Tiles' + }, + { + path: 'layer-filter.html', + title: 'Queryable Layers' + }, + { + path: 'client-filter.html', + title: 'Client-Side Filtering' + }, + { + path: 'utf-grid.html', + title: 'UTF Grid Support' + } + ] + +block content + h1.display-3 Tilegarden: Serverless Mapnik Rendering + p.lead Tilegarden is a serverless tileserver built for AWS Lambda. All the power of a conventional tileserver without needing to worry about provisioning, scaling, or uptime costs. + p.lead Click below for demos of Tilegarden's functionality: + div + .text-center + each page in demoPages + +demoLinks(page) + +