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); };