Skip to content

Commit

Permalink
Add a very basic version of the walking map
Browse files Browse the repository at this point in the history
  • Loading branch information
bradenmacdonald committed Jan 5, 2024
1 parent ab8f8f1 commit a65ef81
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 8 deletions.
18 changes: 14 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import './App.css';
import "maplibre-gl/dist/maplibre-gl.css";
import { Route } from "wouter";
import { Redirect, Route, Switch } from "wouter";

import { Map } from "./Map/Map.tsx";
import { CyclingMap } from './CyclingMap/CyclingMap.tsx';
import { LinkWithQuery } from './components/LinkWithQuery.tsx';
import { Icon } from './components/Icon.tsx';
import { WalkingMap } from './WalkingMap/WalkingMap.tsx';

function App() {

Expand All @@ -14,11 +15,20 @@ function App() {
<Map>
<div className="absolute w-96 bg-white z-50 top-5 left-5 border border-gray-500 rounded shadow-md p-2 flex items-center">
<img src="/transitopia-logo-h.svg" alt="Transitopia" className='block h-10 mr-4' />
<LinkWithQuery href="/transit" className="mx-1 p-1 w-8 h-8 text-center rounded-full bg-gray-50 hover:bg-gray-100" classNameActive="!bg-transitBlue"><Icon icon="bus-front-fill" altText="Transit" /></LinkWithQuery>
<LinkWithQuery href="/pedestrian" className="mx-1 p-1 w-8 h-8 text-center rounded-full bg-gray-50 hover:bg-gray-100" classNameActive="!bg-pedestrianOrange"><Icon icon="person-walking" altText="Walking" /></LinkWithQuery>
<div className="w-8 mx-1"></div>
{/*<LinkWithQuery href="/transit" className="mx-1 p-1 w-8 h-8 text-center rounded-full bg-gray-50 hover:bg-gray-100" classNameActive="!bg-transitBlue"><Icon icon="bus-front-fill" altText="Transit" /></LinkWithQuery>*/}
<LinkWithQuery href="/walking" className="mx-1 p-1 w-8 h-8 text-center rounded-full bg-gray-50 hover:bg-gray-100" classNameActive="!bg-pedestrianOrange"><Icon icon="person-walking" altText="Walking" /></LinkWithQuery>
<LinkWithQuery href="/cycling" className="mx-1 p-1 w-8 h-8 text-center rounded-full bg-gray-50 hover:bg-gray-100" classNameActive="!bg-cyclistGreen"><Icon icon="bicycle" altText="Cycling" /></LinkWithQuery>
</div>
<Route path="/cycling"><CyclingMap /></Route>
<Switch>
<Route path="/cycling"><CyclingMap /></Route>
<Route path="/walking"><WalkingMap /></Route>
<Route path="/"></Route>
<Route>
{/* Not found */}
<Redirect to="/" />
</Route>
</Switch>
</Map>
</>
)
Expand Down
25 changes: 21 additions & 4 deletions src/Map/MapUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,44 @@ export const useMap = () => React.useContext(MapContext)?.map;
* @param layerName Which map layer to watch for the event
* @param handler The handler to call
*/
export function useMapLayerEvent<eventName extends "click"|"mouseenter"|"mouseleave"|"mousemove">(eventName: eventName, layerName: string, handler: (event: maplibregl.MapLayerEventType[eventName]) => void) {
export function useMapLayerEvent<eventName extends "click" | "mouseenter" | "mouseleave" | "mousemove">(eventName: eventName, layerName: string, handler: (event: maplibregl.MapLayerEventType[eventName]) => void) {
const map = useMap();
React.useEffect(() => {
if (!map) return;
map.on(eventName, layerName, handler);
// Cleanup when the handler changes or the component is destroyed:
return () => { map?.off(eventName, layerName, handler); };
})
}, [map, eventName, layerName, handler]);
}

/**
* Register an event handler for a map event
* @param eventName The event to handle
* @param handler The handler to call
*/
export function useMapEvent<eventName extends "zoomend" | "moveend">(eventName: eventName, handler: (event: maplibregl.MapEventType[eventName]) => void) {
export function useMapEvent<eventName extends "load" | "zoomend" | "moveend">(eventName: eventName, handler: (event: maplibregl.MapEventType[eventName]) => void) {
const map = useMap();
React.useEffect(() => {
if (!map) return;
map.on(eventName, handler);
// Cleanup when the handler changes or the component is destroyed:
return () => { map?.off(eventName, handler); };
})
}, [map, eventName, handler]);
}

/**
* React hook to get the current zoom level of the map.
* Using this hook will allow your component to update whenever the zoom changes.
* Note that this only updates *after* the user has finished zooming, not during the zoom.
* Updating throughout the zoom action has major performance problems.
*/
export function useMapZoom() {
// Zoom to assume when we don't yet know the current zoom level of the map (it hasn't loaded yet):
const defaultZoom = 14;
const map = useMap();
const [zoom, setZoom] = React.useState(map?.getZoom() ?? defaultZoom);
const handler = React.useCallback(() => { setZoom(map?.getZoom() ?? defaultZoom); }, [map]);
useMapEvent("zoomend", handler);
useMapEvent("load", handler);
return zoom;
}
37 changes: 37 additions & 0 deletions src/WalkingMap/WalkingMap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from "react";

import { layers } from "./walking-map-layers.ts";
import { useMap, useMapZoom } from "../Map/MapUtils.ts";

export const WalkingMap: React.FC = () => {

const map = useMap();
const zoom = useMapZoom();

// Add the walking layers to the map:
React.useEffect(() => {
if (!map) return;
for (const layer of layers) {
map.addLayer(layer);
}
return () => {
for (const layer of layers) {
map.removeLayer(layer.id);
}
}
});

return <>
<div className="absolute w-96 h-30 bg-white z-50 top-24 left-5 border border-gray-500 rounded shadow-md p-2">
The Transitopia walking map is not yet developed, but for now you can see
all the known pedestrian paths from OpenStreetMap.
</div>
{
zoom < 14 ?
<div className="absolute w-96 h-30 bg-white z-50 top-48 left-5 border border-gray-500 rounded shadow-md p-2 font-bold">
Zoom in to see walkways.
</div>
: null
}
</>;
}
69 changes: 69 additions & 0 deletions src/WalkingMap/walking-map-layers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Transitopia Cycling map style for MapLibre GL
// By Braden MacDonald
// Code license: MIT (see repository's LICENSE file)
// Design license: CC-BY 4.0 https://creativecommons.org/licenses/by/4.0/

import type { LayerSpecification } from "maplibre-gl";
import { defaultLineLayout, interpolateZoom } from "../Map/basemap-layers.ts";

// Changes from Positron:
// - Noto Sans font is removed since our font server can only serve one font at a time - https://github.com/openmaptiles/fonts/issues/17

export const mapSource = "omt-transitopia";


export const layers: LayerSpecification[] = [
// TODO: pedestrian paths under construction
// TODO: Various comfor levels of pedestrian paths
{
// This includes pedestrian paths and cycling paths.
// We show it on the base map but draw in front of it on the cycling and pedestrian maps
id: "walking_path",
type: "line",
source: mapSource,
"source-layer": "transportation",
"filter": [
"all",
["==", "$type", "LineString"],
["==", "class", "path"],
["!=", "subclass", "cycleway"],
],
layout: defaultLineLayout,
"paint": {
"line-color": "rgb(238, 165, 80)",
// "line-opacity": 0.9,
"line-width": interpolateZoom({ z13: 1, z20: 10 }),
},
},
{
id: "walking_path_name",
type: "symbol",
source: mapSource,
"source-layer": "transportation_name",
"filter": [
"all",
["!=", "class", "motorway"],
["==", "$type", "LineString"],
["==", "subclass", "path"],
],
"layout": {
"symbol-placement": "line",
"symbol-spacing": 350,
"text-field": "{name:latin} {name:nonlatin}",
"text-font": ["Metropolis Regular"],
"text-max-angle": 30,
"text-pitch-alignment": "viewport",
"text-rotation-alignment": "map",
"text-size": 10,
"text-transform": "uppercase",
"visibility": "visible",
},
"paint": {
"text-color": "rgb(50, 50, 50)",
"text-halo-blur": 1,
"text-halo-color": "rgba(238, 165, 80, 0.5)",
"text-translate": [0, -10],
"text-halo-width": 2,
},
},
];

0 comments on commit a65ef81

Please sign in to comment.