Created
February 13, 2020 20:31
-
-
Save kbatuigas/21b754c94c4d9f2f4d0eaef9f7944598 to your computer and use it in GitHub Desktop.
Example of Portland area web map displaying neighborhood boundaries and transit stops - OpenLayers, pg_tileserv, pg_featureserv
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<title>Portland</title> | |
<!-- CSS for OpenLayers map --> | |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.2.0/css/ol.css" type="text/css"> | |
<style> | |
html, body, #map-container { | |
margin: 0; | |
width: 100%; | |
height: 100%; | |
font-family: sans-serif; | |
} | |
#popup { | |
border-radius: 5px; | |
padding: 10px; | |
position: relative; | |
background: #fff; | |
border: 1px solid #000; | |
} | |
</style> | |
<!-- Include OpenLayers map --> | |
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.2.0/build/ol.js"></script> | |
</head> | |
<body> | |
<div id="map-container"></div> | |
<!-- popup also needs to be in DOM even though it isn't displayed until click --> | |
<div id="popup"></div> | |
<script> | |
/* Raster tiles for basemap */ | |
const baseLayer = new ol.layer.Tile({ | |
source: new ol.source.XYZ({ | |
url: "https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}.png" | |
}) | |
}); | |
/* Style neighborhood tiles so they stand out more */ | |
const vectorStyle = new ol.style.Style({ | |
stroke: new ol.style.Stroke({ | |
width: 3, | |
color: 'red' | |
}), | |
fill: new ol.style.Fill({ | |
color: 'rgba(255,0,0,0.1)' | |
}) | |
}); | |
// Vector tiles to display neighborhood boundaries | |
/* Neighborhood boundaries data from http://gis-pdx.opendata.arcgis.com/datasets/neighborhoods-regions */ | |
const vectorServer = "http://localhost:7800/"; | |
const vectorSourceLayer = "public.neighborhoods"; | |
const vectorProps = "?properties=id,name,maplabel"; | |
const vectorUrl = vectorServer + vectorSourceLayer + "/{z}/{x}/{y}.pbf" + vectorProps; | |
const vectorLayer = new ol.layer.VectorTile({ | |
source: new ol.source.VectorTile({ | |
format: new ol.format.MVT(), | |
url: vectorUrl | |
}), | |
style: vectorStyle | |
}); | |
// Transit stops feature layer | |
// TriMet stops data from https://developer.trimet.org/gis/ | |
/* TODO: Figure out why not all Portland stops seem to be displaying from | |
pg_fs (the Belmont Av ones are missing, for example), and why stops | |
outside of Portland are still displaying (e.g. Beaverton) even though | |
Portland jurisdiction has been specified below */ | |
const featureSourceLayer = "public.tm_stops"; | |
const LIMIT = "10000"; | |
const jurisdic = "Portland"; | |
const dataUrl = "http://localhost:9000/collections/" + featureSourceLayer + "/items.json?jurisdic=" + | |
jurisdic + "&limit=" + LIMIT; | |
const image = new ol.style.Circle({ | |
radius: 5, | |
fill: new ol.style.Fill({ color: 'blue' }), | |
stroke: new ol.style.Stroke({ | |
color: 'blue', | |
width: 1 | |
}) | |
}); | |
const featureStyle = new ol.style.Style({ | |
image: image | |
}); | |
const featureLayer = new ol.layer.Vector({ | |
source: new ol.source.Vector({ | |
format: new ol.format.GeoJSON(), | |
url: dataUrl | |
}), | |
// TODO: Maybe follow pg_fs UI example and create a style function | |
style: featureStyle | |
}); | |
/* Render map on page */ | |
const map = new ol.Map({ | |
target: 'map-container', | |
layers: [ baseLayer, vectorLayer ], | |
view: new ol.View({ | |
// TODO: Figure out how to just neatly center the map | |
// on load based on whatever area is displayed | |
center: ol.proj.fromLonLat([-122.67, 45.54]), | |
zoom: 12 | |
}) | |
}); | |
map.addLayer(featureLayer); | |
/* Widget displaying stop info */ | |
const overlay = new ol.Overlay({ | |
element: document.getElementById('popup'), | |
offset: [0, -5], | |
positioning: 'bottom-center' | |
}); | |
map.addOverlay(overlay); | |
function displayFeatureInfo (evt) { | |
// Not sure why this is called first | |
overlay.setPosition(); | |
// Why not map.forEachFeatureAtPixel? | |
let features = map.getFeaturesAtPixel(evt.pixel); | |
let loc = evt.coordinate; | |
let info = ""; | |
if (features) { | |
// Neighborhood tiles also get returned as features; | |
// only display popup if it's a stop. Maybe there's | |
// a better way to do this | |
if (features[0].type_ === "Polygon") return; | |
info = features[0].get('stop_id') + ": " + features[0].get('stop_name'); | |
overlay.getElement().innerHTML = info; | |
} | |
overlay.setPosition(loc); | |
} | |
map.on('click', function(evt) { | |
displayFeatureInfo(evt); | |
}); | |
// ADD: | |
// 1. Different color or shape for bus vs MAX stop? | |
// 2. Filtering example? | |
// 3. Add transit lines? | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment