beerbuddy/openlayers/main.js

193 lines
4.9 KiB
JavaScript

import "./style.css";
import { Map, View, Overlay } from "ol";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer";
import { TileJSON, Vector as VectorSource } from "ol/source";
import { useGeographic } from "ol/proj.js";
import { defaults as defaultControls } from "ol/control.js";
import { Popover } from "bootstrap";
import searchBar from "./modules/searchBar.js";
import fetchPins from "./modules/fetchPins.js";
import ButtonControl from "./modules/newButton.js";
import changeResolutionHandler from "./modules/changeResolutionHandler.js";
import pointerMoveHandler from "./modules/pointerMoveHandler.js";
import searchInputHander from "./modules/searchInputHandler.js";
///
const search = searchBar;
let pinning = false;
let popover;
/** Vector source for pins
* @constant {ol.source.VectorSource} */
const pinSource = new VectorSource();
/** Vector layer for pin vector source
* @constant {ol.layer.VectorLayer} */
const pinLayer = new VectorLayer({
source: pinSource,
});
useGeographic();
fetchPins(pinSource);
/** Main map
* @constant {ol.Map} */
const map = new Map({
controls: defaultControls().extend([new ButtonControl()]),
target: document.getElementById("map"),
layers: [
new TileLayer({
source: new TileJSON({
url: `https://api.maptiler.com/maps/hybrid/tiles.json?key=${
import.meta.env.VITE_MAPTILER_KEY
}`,
tileSize: 512,
crossOrigin: "anonymous",
}),
}),
],
view: new View({
center: [0, 0],
zoom: 2,
maxZoom: 20,
}),
});
/** Overlay element for pin popups
* @constant {ol.Overlay} */
const popup = new Overlay({
element: document.getElementById("popup"),
positioning: "bottom-center",
stopEvent: false,
});
map.addOverlay(popup);
map.addControl(search);
const searchInput = search.getInputField();
searchInput.onkeydown = (event) => {
searchInputHander(event, map);
};
/** Removes store information popup from screen
*/
const disposePopover = () => {
if (popover) {
popover.dispose();
popover = undefined;
}
};
/** Handles user clicks. If user clicks on a pixel that contains a pin, display popup with store information */
map.on("click", (event) => {
disposePopover();
const feature = map.forEachFeatureAtPixel(event.pixel, (feature) => feature);
if (!feature) return;
popup.setPosition(event.coordinate);
const element = document.querySelector("#popup");
popover = new Popover(element, {
placement: "top",
html: true,
title: feature.get("store"),
content: function () {
const container = document.createElement("div");
const cheapestItem = document.createElement("div");
cheapestItem.textContent = `Cheapest Item: ${feature.get(
"cheapestItem"
)}`;
container.appendChild(cheapestItem);
const cheapestPerFloz = document.createElement("div");
cheapestPerFloz.textContent = `$ / oz.: ${feature.get("pricePerOz")}`;
container.appendChild(cheapestPerFloz);
const link = document.createElement("a");
link.href = "#";
link.onclick = function (event) {
event.preventDefault();
window.ReactNativeWebView?.postMessage(
`open@${feature.getGeometry().getCoordinates()}`
);
};
link.textContent = "More...";
container.appendChild(link);
return container;
},
});
popover.show();
});
map.on("pointermove", (event) => {
pointerMoveHandler(event, map);
});
map.on("movestart", disposePopover);
map.getView().on("change:resolution", () => {
changeResolutionHandler(pinning, map, pinLayer);
});
/** Centers location on user based on location information passed in from expo-location
* @param {number} lon - User's longitude
* @param {number} lat - User's latitude
*/
window.passLocation = (lon, lat) => {
const view = new View({
center: [lon, lat],
zoom: 18,
maxZoom: 20,
});
map.setView(view);
map.getLayers().extend([pinLayer]);
// sets NEW listener to listen for zoom changes
view.on("change:resolution", () => {
view.getZoom() < 5
? map.getLayers().removeAt(1)
: (() => {
if (map.getLayers().getLength() === 1) {
if (!pinning) map.getLayers().extend([pinLayer]);
}
})();
});
};
/** Begins store creation process if user clicks on "New" button */
window.placePin = () => {
const oldButton = document.querySelector(".new-button > button");
const button = oldButton.cloneNode(true);
oldButton.parentNode.replaceChild(button, oldButton);
pinning = true;
map.getLayers().removeAt(1);
document.getElementById("centerPin").style.display = "block";
button.textContent = "OK";
function OKButtonHandler() {
button.removeEventListener("click", OKButtonHandler, false);
document.getElementById("centerPin").style.display = "none";
window.ReactNativeWebView?.postMessage(
`create@${map.getView().getCenter()}`
);
}
button.addEventListener("click", OKButtonHandler, false);
};