Skip to content

Instantly share code, notes, and snippets.

@tschoffelen
Created September 9, 2023 12:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tschoffelen/0937ec3d446d241caf403f20f0555e41 to your computer and use it in GitHub Desktop.
Save tschoffelen/0937ec3d446d241caf403f20f0555e41 to your computer and use it in GitHub Desktop.
"use client";
import React, { useEffect, useRef } from "react";
import axios from "axios";
const loadMapkitJs = () =>
new Promise((resolve) => {
if (typeof window === undefined) {
return;
}
if ("mapkit" in window && window.mapkit) {
resolve(window.mapkit);
return;
}
const element = document.createElement("script");
element.addEventListener("load", async () => {
if (!("mapkit" in window)) {
return;
}
await window.mapkit.init({
authorizationCallback: async (done) => {
const { data } = await axios.get("/api/frontend/mapkit");
done(data.token);
},
});
resolve(window.mapkit);
});
element.src = "https://cdn.apple-mapkit.com/mk/5.x.x/mapkit.js";
document.head.appendChild(element);
});
const toCompatibleBounds = (region) => ({
getSouthWest: () => [region.southLatitude, region.westLongitude],
getNorthEast: () => [region.northLatitude, region.eastLongitude],
});
const AppleMap = ({
centerPoint,
mapOptions = {},
zoom,
onBoundsChanged,
className,
style,
markers = [],
}) => {
const div = useRef(null);
const mapRef = useRef(null);
const markerRefs = useRef({});
const renderMarkers = (markers) => {
if (!mapRef.current || !markers) {
return;
}
const [map, mapkit] = mapRef.current;
const newAnnotations = [];
for (const marker of markers) {
if (!marker.key) {
throw new Error("Marker must have a key.");
}
if (markerRefs.current[marker.key]) {
continue;
}
const coord = new mapkit.Coordinate(
Number(marker.location.lat),
Number(marker.location.lng)
);
const ann = new mapkit[
marker.marker ? "ImageAnnotation" : "MarkerAnnotation"
](coord, {
data: { key: marker.key },
title: marker.title,
color: marker.color || "#e30a17",
url: marker.marker ? { 1: marker.marker } : undefined,
clusteringIdentifier: "markers",
collisionMode: mapkit.Annotation.CollisionMode.Circle,
displayPriority: mapkit.Annotation.DisplayPriority.Low,
size: marker.marker ? { width: 58, height: 63 } : undefined,
});
newAnnotations.push(ann);
markerRefs.current[marker.key] = marker;
}
if (newAnnotations.length) {
map.addAnnotations(newAnnotations);
}
};
useEffect(() => {
if (!div.current || mapRef.current) {
return;
}
loadMapkitJs().then((mapkit) => {
if (mapRef.current) {
return;
}
div.current.innerHTML = "";
const map = new mapkit.Map(div.current, mapOptions);
mapRef.current = [map, mapkit];
// Set initial region
const zoomLevel = Math.min(21, zoom + 1 || 12);
const delta = (1 / Math.exp(zoomLevel * Math.LN2)) * 360;
map.region = new mapkit.CoordinateRegion(
new mapkit.Coordinate(Number(centerPoint[0]), Number(centerPoint[1])),
new mapkit.CoordinateSpan(delta, delta)
);
map.annotationForCluster = function (clusterAnnnotation) {
clusterAnnnotation.color =
clusterAnnnotation.memberAnnotations[0].color;
};
map.addEventListener("select", function (event) {
markerRefs.current[event.annotation.data.key]?.onClick?.();
});
if (onBoundsChanged) {
onBoundsChanged(toCompatibleBounds(map.region.toBoundingRegion()));
map.addEventListener("region-change-end", function (event) {
console.log("Region Change ended", event);
onBoundsChanged(
toCompatibleBounds(event.target.region.toBoundingRegion())
);
});
}
renderMarkers(markers);
});
}, [div]);
useEffect(() => {
renderMarkers(markers);
}, [markers]);
return (
<div
ref={div}
className={className}
style={style || { height: "100vh", width: "100vw" }}
/>
);
};
export default AppleMap;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment