diff --git a/README.md b/README.md index 8559a12..324a03f 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ Safe Cycling Map ================ -Work in progress! PRs and forks very welcome :) +Work in progress! PRs and forks very welcome :) -![Screenshot of map](img/safe-cycling-map-2022-01-05.jpg) +![Screenshot of map](img/safe-cycling-map-2022-01-05-v2.jpg) A map of bike infrastructure using [osm2streets](https://github.com/a-b-street/osm2streets) output. diff --git a/img/safe-cycling-map-2022-01-05-v2.jpg b/img/safe-cycling-map-2022-01-05-v2.jpg new file mode 100644 index 0000000..af2d703 Binary files /dev/null and b/img/safe-cycling-map-2022-01-05-v2.jpg differ diff --git a/src/App.css b/src/App.css index 781d599..27706bb 100644 --- a/src/App.css +++ b/src/App.css @@ -63,7 +63,7 @@ h1 { z-index: 10; bottom: 0px; left: 10px; - color: #19ff15bf; + color: #039f00bf; font-size: calc(2vw + 2vh + 10px); line-height: 0.8; } diff --git a/src/drawmap.tsx b/src/drawmap.tsx deleted file mode 100644 index 425b3c1..0000000 --- a/src/drawmap.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import mapboxgl from "mapbox-gl"; -import * as http from "https"; - -// southern-most latitude, western-most longitude, northern-most latitude, eastern-most longitude. -export async function getOSMData(bounds: number[]): Promise { - const options = { - hostname: "overpass-api.de", - port: 443, - path: "/api/interpreter", - method: "POST", - headers: { - "Content-Type": "application/json", - }, - }; - console.log("Started POST request..."); - const boundsStr = bounds.join(","); - const request_str = ` - [out:json][timeout:25]; - ( - // query part for: “bicycle_parking=*” - node["bicycle_parking"](${boundsStr}); - way["bicycle_parking"](${boundsStr}); - relation["bicycle_parking"](${boundsStr}); - // query part for: “amenity=bicycle_parking” - node["amenity"="bicycle_parking"](${boundsStr}); - way["amenity"="bicycle_parking"](${boundsStr}); - relation["amenity"="bicycle_parking"](${boundsStr}); - ); - out body; - >; - out skel qt; - `; - console.log("request:", request_str); - - return new Promise((resolve, reject) => { - var req = http.request(options, function (res) { - var body = ""; - res.setEncoding("utf8"); - res.on("data", (chunk) => (body += chunk)); - res.on("end", function () { - if (res.statusCode !== 200) { - console.log("error code", res.statusCode); - reject(res.statusCode); - } - - const jsonResponse = JSON.parse(body); - const bars = jsonResponse.elements; - resolve(bars); - }); - }); - req.on("error", function (e) { - reject(e.message); - }); - req.write(request_str); - req.end(); - }); -} - -export function drawmap(map: mapboxgl.Map): void { - map.addControl(new mapboxgl.NavigationControl()); - map.addControl(new mapboxgl.FullscreenControl()); - // Add geolocate control to the map. - map.addControl( - new mapboxgl.GeolocateControl({ - positionOptions: { - enableHighAccuracy: true, - }, - trackUserLocation: true, - }) - ); - - map.on("moveend", function (originalEvent) { - const { lat, lng } = map.getCenter(); - console.log("A moveend event occurred."); - console.log({ lat, lng }); - - // eg https://localhost:3000 - const location = window.location.origin; - console.log({ location }); - }); -} -export function removeMarkers(markers: mapboxgl.Marker[]): void { - markers.map((marker) => marker.remove()); -} diff --git a/src/layers.ts b/src/layers.ts new file mode 100644 index 0000000..be43d9c --- /dev/null +++ b/src/layers.ts @@ -0,0 +1,205 @@ +function addLayer( + map: mapboxgl.Map, + type: + | "Driving" + | "Parking" + | "Sidewalk" + | "Shoulder" + | "Biking" + | "Bus" + | "SharedLeftTurn" + | "Construction" + | "LightRail" + | "Footway" + | "SharedUse", + paint: mapboxgl.FillPaint | undefined +): void { + map.addLayer({ + id: type, + type: "fill", + source: "osm2streets-vector-tileserver", + "source-layer": "lanePolygons", + paint, + filter: ["==", "type", type], + // filter: ["==", "$type", "Polygon"], + }, 'road-label'); +} + +const colours = { + laneMarking: "white", + intersection: "#666666", + + // Derived from + // https://github.com/a-b-street/osm2streets/blob/5b40c7af877d4314ca7e45c5ac35ec472845c6ca/street-explorer/www/js/layers.js#L55 + Driving: "grey", + // Driving: "black", + Parking: "#333333", + Sidewalk: "#CCCCCC", + Shoulder: "#CCCCCC", + Biking: "#0F7D4B", + Bus: "#BE4A4C", + SharedLeftTurn: "black", + Construction: "#FF6D00", + LightRail: "#844204", + "Buffer(Planters)": "#555555", + + Footway: "#DDDDE8", + SharedUse: "#E5E1BB", +}; + +export const mapOnLoad = (map: mapboxgl.Map) => () => { + const layers = map.getStyle().layers; + // Find the index of the first symbol layer in the map style. + let firstSymbolId; + for (const layer of layers) { + if (layer.type === "symbol") { + firstSymbolId = layer.id; + break; + } + } + console.log({ firstSymbolId }); + + console.log( + "Adding sources. If you don't seen anything check vector server logs." + ); + + // https://docs.mapbox.com/mapbox-gl-js/example/multiple-geometries/ + // Add a new vector tile source with ID 'mapillary'. + map.addSource("osm2streets-vector-tileserver", { + type: "vector", + tiles: ["http://localhost:3000/tile/{z}/{x}/{y}"], + minzoom: 15, + maxzoom: 20, + }); + + // addLayer(map, "LightRail", { + // "fill-color": "yellow", + // "fill-opacity": 0.2, + // }); + + // map.addLayer({ + // id: "geometry", + // type: "fill", + + // source: "osm2streets-vector-tileserver", + // "source-layer": "geometry", + // paint: { + // // To improve! + // "fill-color": colours.Driving, + // "fill-opacity": 0.4, + // }, + // filter: ["==", "$type", "Polygon"], + // }); + + // map.addLayer({ + // id: "lanePolygons", + // type: "fill", + + // source: "osm2streets-vector-tileserver", + // "source-layer": "lanePolygons", + // paint: { + // "fill-color": colours.Driving, + // "fill-opacity": 1, + // }, + // filter: ["==", "$type", "Polygon"], + // }); + + // addLayer(map, "Biking", { + // "fill-color": colours.Biking, + // "fill-opacity": 1, + // }); + + + addLayer(map, "SharedUse", { + "fill-color": "blue", + "fill-opacity": 0.3, + }); + addLayer(map, "Shoulder", { + "fill-color": colours.Shoulder, + "fill-opacity": 0.5, + }); + addLayer(map, "Sidewalk", { + "fill-color": colours.Sidewalk, + "fill-opacity": 0.9, + }); + // Currently on the wrong side of ways?? + addLayer(map, "Footway", { + "fill-color": colours.Footway, + "fill-opacity": 0.9, + }); + addLayer(map, "Driving", { + "fill-color": colours.Driving, + "fill-opacity": 0.9, + }); + + addLayer(map, "Bus", { + "fill-color": colours.Bus, + "fill-opacity": 0.9, + }); + addLayer(map, "Construction", { + "fill-color": colours.Construction, + "fill-opacity": 0.5, + }); + + map.addLayer({ + id: "intersection", + type: "fill", + + source: "osm2streets-vector-tileserver", + "source-layer": "geometry", + paint: { + "fill-color": colours.intersection, + "fill-opacity": 1, + }, + /* + along with `type`, other attributes we could use here are: + - control": "Signed" | "Signalled" | "Uncontrolled" + */ + filter: ["==", "type", "intersection"], + }, 'road-label'); + + // // Currently on the wrong side of ways?? + // addLayer(map, "Parking", { + // "fill-color": 'yellow', + // "fill-opacity": 0.9, + // }); + + map.addLayer({ + id: "Biking", + type: "fill", + source: "osm2streets-vector-tileserver", + "source-layer": "lanePolygons", + paint: { + "fill-color": colours.Biking, + "fill-opacity": 1, + }, + filter: ["==", "type", "Biking"], + // filter: ["==", "type", "Biking"], + }, 'road-label'); + + map.addLayer({ + id: "intersectionMarkings", + type: "fill", + + source: "osm2streets-vector-tileserver", + "source-layer": "intersectionMarkings", + paint: { + "fill-color": colours.Driving, + "fill-opacity": 0.8, + }, + filter: ["==", "$type", "Polygon"], + }, 'road-label'); + + map.addLayer({ + id: "laneMarkings", + type: "fill", + + source: "osm2streets-vector-tileserver", + "source-layer": "laneMarkings", + paint: { + "fill-color": colours.laneMarking, + "fill-opacity": 1, + }, + filter: ["==", "$type", "Polygon"], + }, 'road-label'); +}; \ No newline at end of file diff --git a/src/map.tsx b/src/map.tsx index d8d3de1..2d20734 100644 --- a/src/map.tsx +++ b/src/map.tsx @@ -4,26 +4,20 @@ import React, { useRef, useEffect, useState } from "react"; // import mapboxgl from "!mapbox-gl"; import mapboxgl from "mapbox-gl"; import "./App.css"; +import { mapOnLoad } from "./layers"; const MAPBOX_TOKEN = "pk.eyJ1IjoiamFrZWMiLCJhIjoiY2tkaHplNGhjMDAyMDJybW4ybmRqbTBmMyJ9.AR_fnEuka8-cFb4Snp3upw"; mapboxgl.accessToken = MAPBOX_TOKEN; -const colours = { - road: "grey", - cycleway: "green", - laneMarking: "white", -}; - -type LoadingStatusType = "loading" | "success" | "429error" | "unknownerror"; export function Map() { const mapContainer = React.useRef(null); const mapRef = React.useRef(null); - const markers = useRef([]); - const [lng, setLng] = useState(151.20671); - const [lat, setLat] = useState(-33.8683861); - const [zoom, setZoom] = useState(16); + + const [lng, setLng] = useState(151.21084276742022); + const [lat, setLat] = useState(-33.8720286260115); + const [zoom, setZoom] = useState(17); useEffect(() => { // This is called on every pan @@ -36,90 +30,14 @@ export function Map() { mapRef.current = new mapboxgl.Map({ container: mapContainer.current, - style: "mapbox://styles/mapbox/dark-v10", + style: "mapbox://styles/mapbox/streets-v12", + // style: "mapbox://styles/mapbox/dark-v10", center: [lng, lat], zoom: zoom, }); + const map = mapRef.current; - map.on("load", function () { - console.log( - "Adding sources. If you don't seen anything check vector server logs." - ); - - // https://docs.mapbox.com/mapbox-gl-js/example/multiple-geometries/ - // Add a new vector tile source with ID 'mapillary'. - map.addSource("osm2streets-vector-tileserver", { - type: "vector", - tiles: ["http://localhost:3000/tile/{z}/{x}/{y}"], - minzoom: 15, - maxzoom: 22, - }); - - map.addLayer({ - id: "geometry", - type: "fill", - - source: "osm2streets-vector-tileserver", - "source-layer": "geometry", - paint: { - // To improve! - "fill-color": colours.road, - "fill-opacity": 0.4, - }, - filter: ["==", "$type", "Polygon"], - }); - - map.addLayer({ - id: "lanePolygons", - type: "fill", - - source: "osm2streets-vector-tileserver", - "source-layer": "lanePolygons", - paint: { - "fill-color": colours.road, - "fill-opacity": 1, - }, - filter: ["==", "$type", "Polygon"], - }); - map.addLayer({ - id: "bikePath", - type: "fill", - - source: "osm2streets-vector-tileserver", - "source-layer": "lanePolygons", - paint: { - "fill-color": colours.cycleway, - "fill-opacity": 1, - }, - filter: ["==", "type", "Biking"], - // filter: ["==", "$type", "Polygon"], - }); - - map.addLayer({ - id: "laneMarkings", - type: "fill", - - source: "osm2streets-vector-tileserver", - "source-layer": "laneMarkings", - paint: { - "fill-color": colours.laneMarking, - "fill-opacity": 1, - }, - filter: ["==", "$type", "Polygon"], - }); - map.addLayer({ - id: "intersectionMarkings", - type: "fill", - - source: "osm2streets-vector-tileserver", - "source-layer": "intersectionMarkings", - paint: { - "fill-color": colours.road, - "fill-opacity": 0.8, - }, - filter: ["==", "$type", "Polygon"], - }); - }); + map.on("load", mapOnLoad(map)); map.addControl(new mapboxgl.NavigationControl()); map.addControl(new mapboxgl.FullscreenControl()); @@ -136,6 +54,9 @@ export function Map() { if (!map) { return; // wait for map to initialize } + const { lng, lat } = map.getCenter(); + const zoom = map.getZoom(); + console.log(lng, lat, zoom); setLng(map.getCenter().lng); setLat(map.getCenter().lat); @@ -143,12 +64,11 @@ export function Map() { }); }); - return (