Skip to content

Commit

Permalink
Merge pull request #178 from kieler/poi_icon_scaling
Browse files Browse the repository at this point in the history
Poi icon scaling
  • Loading branch information
n1kPLV authored Sep 22, 2023
2 parents 092f1c1 + 0bd9e0a commit 011195b
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 15 deletions.
81 changes: 69 additions & 12 deletions Website/src/app/components/map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ import { PointOfInterest, POIType, POITypeIconValues } from "@/utils/api";
import { POIIconImg } from "@/utils/common";
import TrackerCharge from "@/app/components/tracker";

/**
* The side lengths of the poi icons in rem
*/
const POI_ICON_SIZES = {
tiny: 0.5,
small: 1,
medium: 2,
large: 3,
xl: 4
} as const;

/**
* Constructs the content of the popup for a POI, without React
* @param poi The POI to construct the popup for
Expand Down Expand Up @@ -50,17 +61,28 @@ function Map({

// We also need state for the center of the map, the vehicle in focus and the container containing the contents of an open popup
const [position, setPosition] = useState(initial_position);
const [zoomLevel, setZoomLevel] = useState(initial_zoom_level);
const [popupContainer, setPopupContainer] = useState(undefined as undefined | HTMLDivElement);

// find the vehicle that is in focus, but only if either the vehicles, or the focus changes.
const vehicleInFocus = useMemo(() => vehicles.find(v => v.id == focus), [vehicles, focus]);

// derive the appropriate POI Icon size from the zoom level. These are arbitrarily chosen values that seemed right to me
const poiIconSize: keyof typeof POI_ICON_SIZES =
zoomLevel < 8 ? "tiny" : zoomLevel < 12 ? "small" : zoomLevel < 14 ? "medium" : zoomLevel < 16 ? "large" : "xl";

const poiIconSideLength = POI_ICON_SIZES[poiIconSize];

// create icons for each poi type
const enriched_poi_types: (POIType & { leaf_icon: L.Icon })[] = useMemo(
() =>
poi_types.map(pt => {
const icon_src = POIIconImg[pt.icon] ?? POIIconImg[POITypeIconValues.Generic];
const leaf_icon = L.icon({ iconUrl: icon_src, iconSize: [45, 45] });

// set an initial icon size, will be modified in via css
const iconSize: [number, number] = [45, 45];

const leaf_icon = L.icon({ iconUrl: icon_src, iconSize, className: "poi-icon transition-all" });

return {
...pt,
Expand All @@ -70,8 +92,6 @@ function Map({
[poi_types]
);

// debugger;

/** handling the initialization of leaflet. MUST NOT be called twice. */
function insertMap() {
assert(mapContainerRef.current, "Error: Ref to Map Container not populated");
Expand All @@ -91,21 +111,42 @@ function Map({
poiPane.style.zIndex = "550";
poiPane.classList.add("leaflet-marker-pane");
// as POIs don't have shadows, we don't need a poiShadowPane.
}

/**
* Add appropriate event listeners to the map
*/
function addMapEvents() {
const map = mapRef.current;
assert(map != undefined, "Error: Map not ready!");

/*const openrailwaymap = L.tileLayer('http://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png',
{
attribution: '<a href="https://www.openstreetmap.org/copyright">© OpenStreetMap contributors</a>, Style: <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA 2.0</a> <a href="http://www.openrailwaymap.org/">OpenRailwayMap</a> and OpenStreetMap',
minZoom: 2,
maxZoom: 19,
tileSize: 256
}).addTo(mapRef.current);*/
map.addEventListener("moveend", () => {
// prevent infinite loops by checking that the map actually moved
const newPos = map.getCenter();
setPosition(oldPos => {
if (newPos.lng !== oldPos.lng || newPos.lat !== oldPos.lat) {
return {
lat: newPos.lat,
lng: newPos.lng
};
}
return oldPos;
});
});

map.addEventListener("zoomend", () => {
// React can automatically debounce this, as zoom level is just a number.
const newZoomLevel = map.getZoom();

setZoomLevel(newZoomLevel);
});
}

/** Set the zoom level of the map */
function setMapZoom() {
assert(mapRef.current != undefined, "Error: Map not ready!");

mapRef.current.setZoom(initial_zoom_level);
mapRef.current.setZoom(zoomLevel);
}

/** Set the center of the map. The zoom level MUST be set before, otherwise leaflet will crash. */
Expand Down Expand Up @@ -239,12 +280,28 @@ function Map({

// Schedule various effects (JS run after the page is rendered) for changes to various state variables.
useEffect(insertMap, []);
useEffect(setMapZoom, [initial_zoom_level]);
useEffect(addMapEvents, [setPosition, setZoomLevel]);
useEffect(setMapZoom, [zoomLevel]);
useEffect(setMapPosition, [position]);
useEffect(addTrackPath, [track_data?.path, track_data]);
useEffect(updateMarkers, [focus, setFocus, vehicles]);
useEffect(addPOIs, [points_of_interest, enriched_poi_types]);

// set the width and height of all poi icons using an effect to prevent re-rendering the icons
useEffect(() => {
// Iterate over all poi icons currently present
for (const poiIcon of document.querySelectorAll(".poi-icon")) {
if (poiIcon instanceof HTMLElement) {
// set the height and width using inline styles.
// this will probably make this component much more fragile than it needs to be...
poiIcon.style.width = poiIcon.style.height = `${poiIconSideLength}rem`;

// we also need to adjust the margins, so that the icons remain centered
poiIcon.style.marginLeft = poiIcon.style.marginTop = `${-poiIconSideLength / 2}rem`;
}
}
}, [points_of_interest, enriched_poi_types, poiIconSideLength]);

return (
<>
<div id="map" className="absolute inset-0" ref={mapContainerRef} />
Expand Down
5 changes: 2 additions & 3 deletions Website/src/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { LatLngExpression } from "leaflet";
import { FullTrack, PointOfInterest, POIType, Vehicle } from "./api";
import { FullTrack, PointOfInterest, POIType, Position, Vehicle } from "./api";
import { Dispatch, JSX, SetStateAction } from "react";

export interface MapConfig {
initial_position: LatLngExpression;
initial_position: Position;
initial_zoom_level: number;
vehicles: Vehicle[];
track_data?: FullTrack;
Expand Down

0 comments on commit 011195b

Please sign in to comment.