kopia lustrzana https://github.com/jakecoppinger/safe-cycling-map
Improve layering, update screenshot
rodzic
5769ebfb2a
commit
012c211654
|
@ -1,10 +1,10 @@
|
||||||
Safe Cycling Map
|
Safe Cycling Map
|
||||||
================
|
================
|
||||||
|
|
||||||
Work in progress! PRs and forks very welcome :)
|
Work in progress! PRs and forks very welcome :)
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
A map of bike infrastructure using [osm2streets](https://github.com/a-b-street/osm2streets) output.
|
A map of bike infrastructure using [osm2streets](https://github.com/a-b-street/osm2streets) output.
|
||||||
|
|
||||||
|
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 348 KiB |
|
@ -63,7 +63,7 @@ h1 {
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
color: #19ff15bf;
|
color: #039f00bf;
|
||||||
font-size: calc(2vw + 2vh + 10px);
|
font-size: calc(2vw + 2vh + 10px);
|
||||||
line-height: 0.8;
|
line-height: 0.8;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<any> {
|
|
||||||
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());
|
|
||||||
}
|
|
|
@ -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');
|
||||||
|
};
|
108
src/map.tsx
108
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 mapboxgl from "mapbox-gl";
|
import mapboxgl from "mapbox-gl";
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
|
import { mapOnLoad } from "./layers";
|
||||||
|
|
||||||
const MAPBOX_TOKEN =
|
const MAPBOX_TOKEN =
|
||||||
"pk.eyJ1IjoiamFrZWMiLCJhIjoiY2tkaHplNGhjMDAyMDJybW4ybmRqbTBmMyJ9.AR_fnEuka8-cFb4Snp3upw";
|
"pk.eyJ1IjoiamFrZWMiLCJhIjoiY2tkaHplNGhjMDAyMDJybW4ybmRqbTBmMyJ9.AR_fnEuka8-cFb4Snp3upw";
|
||||||
|
|
||||||
mapboxgl.accessToken = MAPBOX_TOKEN;
|
mapboxgl.accessToken = MAPBOX_TOKEN;
|
||||||
|
|
||||||
const colours = {
|
|
||||||
road: "grey",
|
|
||||||
cycleway: "green",
|
|
||||||
laneMarking: "white",
|
|
||||||
};
|
|
||||||
|
|
||||||
type LoadingStatusType = "loading" | "success" | "429error" | "unknownerror";
|
|
||||||
export function Map() {
|
export function Map() {
|
||||||
const mapContainer = React.useRef<HTMLDivElement>(null);
|
const mapContainer = React.useRef<HTMLDivElement>(null);
|
||||||
const mapRef = React.useRef<mapboxgl.Map | null>(null);
|
const mapRef = React.useRef<mapboxgl.Map | null>(null);
|
||||||
const markers = useRef<mapboxgl.Marker[]>([]);
|
|
||||||
const [lng, setLng] = useState(151.20671);
|
const [lng, setLng] = useState(151.21084276742022);
|
||||||
const [lat, setLat] = useState(-33.8683861);
|
const [lat, setLat] = useState(-33.8720286260115);
|
||||||
const [zoom, setZoom] = useState(16);
|
const [zoom, setZoom] = useState(17);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// This is called on every pan
|
// This is called on every pan
|
||||||
|
@ -36,90 +30,14 @@ export function Map() {
|
||||||
|
|
||||||
mapRef.current = new mapboxgl.Map({
|
mapRef.current = new mapboxgl.Map({
|
||||||
container: mapContainer.current,
|
container: mapContainer.current,
|
||||||
style: "mapbox://styles/mapbox/dark-v10",
|
style: "mapbox://styles/mapbox/streets-v12",
|
||||||
|
// style: "mapbox://styles/mapbox/dark-v10",
|
||||||
center: [lng, lat],
|
center: [lng, lat],
|
||||||
zoom: zoom,
|
zoom: zoom,
|
||||||
});
|
});
|
||||||
|
|
||||||
const map = mapRef.current;
|
const map = mapRef.current;
|
||||||
map.on("load", function () {
|
map.on("load", mapOnLoad(map));
|
||||||
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.addControl(new mapboxgl.NavigationControl());
|
map.addControl(new mapboxgl.NavigationControl());
|
||||||
map.addControl(new mapboxgl.FullscreenControl());
|
map.addControl(new mapboxgl.FullscreenControl());
|
||||||
|
@ -136,6 +54,9 @@ export function Map() {
|
||||||
if (!map) {
|
if (!map) {
|
||||||
return; // wait for map to initialize
|
return; // wait for map to initialize
|
||||||
}
|
}
|
||||||
|
const { lng, lat } = map.getCenter();
|
||||||
|
const zoom = map.getZoom();
|
||||||
|
console.log(lng, lat, zoom);
|
||||||
|
|
||||||
setLng(map.getCenter().lng);
|
setLng(map.getCenter().lng);
|
||||||
setLat(map.getCenter().lat);
|
setLat(map.getCenter().lat);
|
||||||
|
@ -143,12 +64,11 @@ export function Map() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="sidebar">
|
<div className="sidebar">
|
||||||
<label>
|
<label>
|
||||||
A side-project by{" "}
|
A work in progrss side project by{" "}
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
|
@ -156,7 +76,7 @@ export function Map() {
|
||||||
>
|
>
|
||||||
Jake Coppinger
|
Jake Coppinger
|
||||||
</a>{" "}
|
</a>{" "}
|
||||||
| Open source (GPLv3) on{" "}
|
| Open source on{" "}
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
|
|
Ładowanie…
Reference in New Issue