Skip to content

Commit

Permalink
Yet another map refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
krasch committed Dec 20, 2024
1 parent 6be7612 commit ec22122
Show file tree
Hide file tree
Showing 9 changed files with 492 additions and 859 deletions.
7 changes: 4 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@
<script src="script/components/calendar/calendar.js"></script>
<script src="script/components/calendar/draganddrop.js"></script>
<script src="script/components/map/map.js"></script>
<script src="script/components/map/mapObjects.js"></script>
<script src="script/components/map/animation.js"></script>
<script src="script/components/map/cities.js"></script>
<script src="script/components/map/edges.js"></script>
<script src="script/components/map/util.js"></script>

<title>Trans-Europe-Planner</title>
<link rel="stylesheet" href="style/sidebar.css">
Expand Down Expand Up @@ -95,7 +96,7 @@
</template>

<template id="template-city-marker-home">
<div class="city-marker-home"></div>
<div class="city-marker-home" data-city-id="to-be-filled"></div>
</template>

<template id="template-city-marker-destination">
Expand Down
42 changes: 0 additions & 42 deletions script/components/map/animation.js

This file was deleted.

241 changes: 241 additions & 0 deletions script/components/map/cities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
function initHomeMarker(id, lngLat) {
const element = createElementFromTemplate("template-city-marker-home", {
$root$: { "data-city-id": id },
});

const marker = new maplibregl.Marker({
element: element,
anchor: "bottom",
});
marker.setLngLat(lngLat);
return marker;
}

function initDestinationMarker(lngLat) {
const marker = new maplibregl.Marker({
element: createElementFromTemplate("template-city-marker-destination"),
anchor: "bottom",
});
marker.setLngLat(lngLat);
return marker;
}

function initCityMenu(id, name, lngLat) {
const element = createElementFromTemplate("template-city-menu", {
$root$: { "data-city-id": id, "data-city-name": name },
".title": { innerText: name },
});

const popup = new maplibregl.Popup({
anchor: "left",
offset: [5, 0],
closeButton: true,
});
popup.setDOMContent(element).setLngLat(lngLat);

const buttonShowRoutes = element.querySelector("button[value='showRoutes']");
const buttonMakeCut = element.querySelector("button[value='makeCut']");

popup.updateElement = (state) => {
if (state.isDestination !== undefined)
updateVisibility(buttonShowRoutes, state.isDestination);
};

return popup;
}

function showStartAnimation(map, geo, initialState, animationDoneCallback) {
const homeMarkers = [];
const destinationMarkers = [];

for (let id in initialState) {
if (initialState[id].isHome)
homeMarkers.push(initHomeMarker(id, geo[id].lngLat));
if (initialState[id].isDestination)
destinationMarkers.push(initDestinationMarker(geo[id].lngLat));
}

//this is the second animation we'll do (show destination markers dropping)
const animateDestinations = () =>
animateDropWithBounce(
map,
destinationMarkers,
200,
3,
() => animationDoneCallback(destinationMarkers), // when animation is done callback to main
);

// run the first animation (show home marker(s) dropping)
animateDropWithBounce(
map,
homeMarkers,
300,
3,
animateDestinations, // when that is done do the second animation
);
}

ANIMATION = true;

class Cities {
#callbacks = {
mouseOver: () => {},
mouseLeave: () => {},
click: () => {},
menuClick: () => {},
};

#source = "cities";
#layers = ["city-name", "city-circle-interact"];

#keys = {
featureState: [
"hover",
"isVisible",
"isDestination",
"isStop",
"circleColor",
],
cityMenu: ["isDestination"],
sourceData: ["rank", "isVisible"], // slow to update
};

#map;
#geo; // {id: {name: , lngLat: }}

#state;
#homeMarkers = {};
#menus = {};

#pulsars = null;

constructor(map, geo, initialState) {
this.#map = map;
this.#geo = geo;

this.#state = new StateDict();
const events = new MouseEventHelper(this.#map, this.#layers);

events.on("mouseOver", (id, lngLat) => {
this.#map.getCanvas().style.cursor = "pointer";
this.#stopAnimation();
this.setHover(id, true);
this.#callbacks["mouseOver"](id);
});

events.on("mouseLeave", (id, lngLat) => {
this.#map.getCanvas().style.cursor = "default";
this.setHover(id, false);
this.#callbacks["mouseLeave"](id);
});

events.on("click", (id, lngLat) => {
this.#showCityMenu(id);
this.#callbacks["click"](id);
});

this.#map._container.addEventListener("click", (e) => {
// menu item was clicked
if (e.target.tagName === "BUTTON") {
const menu = e.target.parentElement.parentElement;
const id = menu.dataset.cityId;
const name = menu.dataset.cityName;
this.#hideCityMenu(id);
this.#callbacks["menuClick"](id, name, e.target.value);
}
// home marker was clicked
else if (e.target.classList.contains("city-marker-home")) {
const id = e.target.dataset.cityId;
this.#showCityMenu(id);
this.#callbacks["click"](id);
}
});

// initial drawing
if (ANIMATION) {
showStartAnimation(this.#map, geo, initialState, (pulsars) => {
this.#pulsars = pulsars;
this.update(initialState);
});
} else {
this.update(initialState);
}
}

on(eventName, callback) {
this.#callbacks[eventName] = callback;
}

update(updates) {
// apply update to the state
// changes contains the "true" changes, i.e. things that actually changed
const changes = this.#state.update(updates);

this.#updateFeatureState(changes);
this.#updateSourceData(changes);

for (let change of changes) {
if (change.key === "isHome")
this.#updateHomeMarker(change.id, change.value);
}
}

setHover(id, state) {
this.#state.set(id, "hover", state);
this.#copyStateToFeatureState(id);
}

#updateFeatureState(changes) {
const filtered = filterChanges(changes, this.#keys.featureState);
const grouped = groupChangesById(filtered);

for (let id in grouped) this.#copyStateToFeatureState(id);
}

#updateSourceData(changes) {
const filtered = filterChanges(changes, this.#keys.sourceData);
const grouped = groupChangesById(filtered);
updateSourceData(this.#map, this.#source, grouped);
}

#updateHomeMarker(id, isHome) {
if (isHome && !this.#homeMarkers[id]) {
this.#homeMarkers[id] = initHomeMarker(id, this.#geo[id].lngLat);
this.#homeMarkers[id].addTo(this.#map);
}

if (!isHome && this.#homeMarkers[id]) {
this.#homeMarkers[id].remove();
delete this.#homeMarkers[id];
}
}

#copyStateToFeatureState(id) {
const current = this.#state.getAll(id, this.#keys.featureState);
this.#map.setFeatureState({ source: this.#source, id: id }, current);
}

#showCityMenu(id) {
if (!this.#menus[id]) {
this.#menus[id] = initCityMenu(
id,
this.#geo[id].name,
this.#geo[id].lngLat,
);
}

const current = this.#state.getAll(id, this.#keys.cityMenu);
this.#menus[id].updateElement(current);
this.#menus[id].addTo(this.#map);
}

#hideCityMenu(id) {
this.#menus[id].remove();
}

#stopAnimation() {
if (this.#pulsars) {
for (let p of this.#pulsars) p.remove();
}
}
}
Loading

0 comments on commit ec22122

Please sign in to comment.