diff --git a/apps/infoalloggi/src/components/MapDialog.tsx b/apps/infoalloggi/src/components/MapDialog.tsx index c588d8a..96b246f 100644 --- a/apps/infoalloggi/src/components/MapDialog.tsx +++ b/apps/infoalloggi/src/components/MapDialog.tsx @@ -1,5 +1,5 @@ "use client"; -import { MapIcon } from "lucide-react"; +import { MapIcon, Ruler } from "lucide-react"; import dynamic from "next/dynamic"; import { useState } from "react"; import { Button } from "~/components/ui/button"; @@ -26,6 +26,7 @@ export const MappaDialogFullscreen = ({ }) => { const { t } = useTranslation(); const [open, setOpen] = useState(false); + const [isMeasuring, setIsMeasuring] = useState(false); return ( @@ -38,11 +39,20 @@ export const MappaDialogFullscreen = ({ {t.mappa} {t.mappa} +
{lat && long && ( <> void; + isMeasuring?: boolean; } & (GroupProps | SingleProps); const defaults = { @@ -63,6 +68,144 @@ const MedianPoint = (pos: posData[]) => { return { lat, lng }; }; +const MarkerComp = ({ + pos, + markerType, + markerRadius, +}: { + pos: posData; + markerType: MarkerType; + markerRadius?: number; +}) => { + switch (markerType) { + case "price": + return ( + + {pos.markerTxt} + + } + size="sm" + /> + } + position={pos.pos} + title={pos.markerTxt} + > + {pos.popupTxt && {pos.popupTxt}} + + ); + + case "precise": + return ( + + {pos.popupTxt && {pos.popupTxt}} + + ); + case "circle": + return ( + + {pos.popupTxt && {pos.popupTxt}} + + ); + case "home": + return ( + <> + + {pos.popupTxt && {pos.popupTxt}} + + + +
+ } + position={pos.pos} + title={pos.markerTxt} + > + {pos.popupTxt && {pos.popupTxt}} + + + ); + default: + throw new Error(`Invalid marker type: ${markerType satisfies never}`); + } +}; + +function getDistanceInKm(pos1: LatLon, pos2: LatLon) { + const R = 6371; // Radius of the earth in km + const dLat = deg2rad(pos2.lat - pos1.lat); + const dLon = deg2rad(pos2.lng - pos1.lng); + + const a = + Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(deg2rad(pos1.lat)) * + Math.cos(deg2rad(pos2.lat)) * + Math.sin(dLon / 2) * + Math.sin(dLon / 2); + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + const distance = R * c; // Distance in km + return distance; +} + +function deg2rad(deg: number) { + return deg * (Math.PI / 180); +} + +const DistanceMarker = ({ pos, posix }: { pos: LatLon; posix: posData }) => { + const distanceKm = getDistanceInKm(pos, posix.pos); + return ( + <> + + ~{distanceKm.toFixed(2)} km + + } + size="sm" + /> + } + position={pos} + title={"Distanza"} + /> + + + + ); +}; + const MapComp = ({ posix, autocenter, @@ -75,10 +218,16 @@ const MapComp = ({ isGroup, groupData, onMapClick, + isMeasuring, }: MapProps) => { + const [measuringPos, setMeasuringPos] = useState(null); const APIs = () => { useMapEvents({ - click: () => { + click: (e) => { + if (isMeasuring) { + setMeasuringPos(e.latlng); + return; + } if (onMapClick) { onMapClick(); } @@ -87,82 +236,6 @@ const MapComp = ({ return null; }; - const MarkerComp = ({ pos }: { pos: posData }) => { - switch (markerType) { - case "price": - return ( - - {pos.markerTxt} - - } - size="sm" - /> - } - position={pos.pos} - title={pos.markerTxt} - > - {pos.popupTxt && {pos.popupTxt}} - - ); - - case "precise": - return ( - - {pos.popupTxt && {pos.popupTxt}} - - ); - case "circle": - return ( - - {pos.popupTxt && {pos.popupTxt}} - - ); - case "home": - return ( - <> - - {pos.popupTxt && {pos.popupTxt}} - - - - - } - position={pos.pos} - title={pos.markerTxt} - > - {pos.popupTxt && {pos.popupTxt}} - - - ); - default: - throw new Error(`Invalid marker type: ${markerType satisfies never}`); - } - }; - const hashKey = JSON.stringify( groupData ? groupData.map((p) => p.pos) : posix.pos, ); @@ -190,10 +263,24 @@ const MapComp = ({ /> {isGroup ? ( - // biome-ignore lint/suspicious/noArrayIndexKey: - groupData.map((pos, i) => ) + groupData.map((pos, i) => ( + + key={i} + markerRadius={markerRadius} + markerType={markerType} + pos={pos} + /> + )) ) : ( - + + )} + {isMeasuring && measuringPos && ( + )} ); diff --git a/apps/infoalloggi/src/components/map/map_marker.tsx b/apps/infoalloggi/src/components/map/map_marker.tsx index 5b89447..6ebf98b 100644 --- a/apps/infoalloggi/src/components/map/map_marker.tsx +++ b/apps/infoalloggi/src/components/map/map_marker.tsx @@ -4,10 +4,64 @@ import { cn } from "~/lib/utils"; interface MapMarkerProps { label: ReactNode; - color?: "primary" | "red" | "blue" | "green" | "yellow" | "purple" | "gray"; + color?: keyof typeof colorClasses; className?: string; - size?: "sm" | "md" | "lg"; + size?: keyof typeof sizeClasses; } +const colorClasses = { + blue: { + rectangle: "bg-blue-500 text-white", + triangle: "border-t-blue-500", + }, + gray: { + rectangle: "bg-gray-500 text-white", + triangle: "border-t-gray-500", + }, + green: { + rectangle: "bg-green-500 text-white", + triangle: "border-t-green-500", + }, + primary: { + rectangle: "bg-[#242428] text-white", + triangle: "border-t-[#242428]", + }, + purple: { + rectangle: "bg-purple-500 text-white", + triangle: "border-t-purple-500", + }, + red: { + rectangle: "bg-red-500 text-white", + triangle: "border-t-red-500", + }, + yellow: { + rectangle: "bg-yellow-500 text-black", + triangle: "border-t-yellow-500", + }, + orange: { + rectangle: "bg-orange-500 text-white", + triangle: "border-t-orange-500", + }, +} as const; +const sizeClasses = { + lg: { + container: "h-16", + rectangle: "px-4 py-3 rounded-xl", + text: "text-base", + triangle: "border-l-[12px] border-r-[12px] border-t-[20px]", + }, + md: { + container: "h-12", + rectangle: "px-3 py-2 rounded-lg", + text: "text-sm", + triangle: "border-l-[10px] border-r-[10px] border-t-[16px]", + }, + sm: { + container: "h-8", + rectangle: "px-2 py-1 rounded-md", + text: "text-xs", + triangle: "border-l-[8px] border-r-[8px] border-t-[15px]", + }, +} as const; export default function MapMarker({ label, @@ -15,62 +69,10 @@ export default function MapMarker({ className, size = "md", }: MapMarkerProps) { - const colorClasses = { - blue: { - rectangle: "bg-blue-500 text-white", - triangle: "border-t-blue-500", - }, - gray: { - rectangle: "bg-gray-500 text-white", - triangle: "border-t-gray-500", - }, - green: { - rectangle: "bg-green-500 text-white", - triangle: "border-t-green-500", - }, - primary: { - rectangle: "bg-[#242428] text-white", - triangle: "border-t-[#242428]", - }, - purple: { - rectangle: "bg-purple-500 text-white", - triangle: "border-t-purple-500", - }, - red: { - rectangle: "bg-red-500 text-white", - triangle: "border-t-red-500", - }, - yellow: { - rectangle: "bg-yellow-500 text-black", - triangle: "border-t-yellow-500", - }, - }; - - const sizeClasses = { - lg: { - container: "h-16", - rectangle: "px-4 py-3 rounded-xl", - text: "text-base", - triangle: "border-l-[12px] border-r-[12px] border-t-[12px]", - }, - md: { - container: "h-12", - rectangle: "px-3 py-2 rounded-lg", - text: "text-sm", - triangle: "border-l-[10px] border-r-[10px] border-t-[10px]", - }, - sm: { - container: "h-8", - rectangle: "px-2 py-1 rounded-md", - text: "text-xs", - triangle: "border-l-[8px] border-r-[8px] border-t-[8px]", - }, - }; - return (