|
1 | 1 | <template>
|
2 |
| - <div class="map card-style"> |
3 |
| - <div |
4 |
| - ref="map" |
5 |
| - id="map-div" |
6 |
| - class="w-full h-full select-none saturate-[1.15] dark:hue-rotate-180 dark:invert rounded-md sm:rounded-lg" |
7 |
| - :alt="$t('components.media-map.img-alt-text')" |
8 |
| - ></div> |
9 |
| - <div |
10 |
| - :key="rerenderKey" |
11 |
| - class="flex flex-col items-center justify-center h-full px-5 pb-5 text-2xl text-center space-y-5 text-light-cta-orange dark:text-dark-cta-orange" |
12 |
| - :class="{ hidden: !errorOccurred }" |
13 |
| - > |
14 |
| - <p>{{ $t("components.media-map.error-message") }}</p> |
15 |
| - <p>{{ $t("components.media-map.sorry-message") }}</p> |
16 |
| - </div> |
17 |
| - </div> |
| 2 | + <div class="card-style"></div> |
18 | 3 | </template>
|
19 |
| - |
20 |
| -<script setup lang="ts"> |
21 |
| -import type { MapOptions } from "leaflet"; |
22 |
| -import L from "leaflet"; |
23 |
| -import "leaflet/dist/leaflet.css"; |
24 |
| -
|
25 |
| -const props = defineProps<{ |
26 |
| - addresses: string[]; |
27 |
| - title: string; |
28 |
| - type: string; |
29 |
| -}>(); |
30 |
| -
|
31 |
| -const rerenderKey = ref(0); |
32 |
| -const map = ref(); |
33 |
| -
|
34 |
| -type Marker = { |
35 |
| - address: string; |
36 |
| - lat: number; |
37 |
| - lon: number; |
38 |
| -}; |
39 |
| -
|
40 |
| -let errorOccurred: boolean = false; |
41 |
| -
|
42 |
| -function handleMapError(error: Error) { |
43 |
| - console.error(error); |
44 |
| - errorOccurred = true; |
45 |
| -
|
46 |
| - // TODO: More helpful and better looking error messages. |
47 |
| - rerenderKey.value += 1; // rerender the error div |
48 |
| - map.value.style.opacity = 0; |
49 |
| - map.value.style.position = "absolute"; |
50 |
| -} |
51 |
| -
|
52 |
| -function drawMap(avgLat: number, avgLon: number, markers: Marker[]) { |
53 |
| - const mapOptions: MapOptions = { |
54 |
| - center: [avgLat, avgLon], |
55 |
| - zoom: 13, |
56 |
| - attributionControl: false, |
57 |
| - }; |
58 |
| -
|
59 |
| - const leafletMap = L.map("map-div", mapOptions); |
60 |
| -
|
61 |
| - const layer = new L.TileLayer( |
62 |
| - "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" |
63 |
| - ); |
64 |
| - leafletMap.addLayer(layer); |
65 |
| -
|
66 |
| - const colorMode = useColorMode(); |
67 |
| - let eventColor = ""; |
68 |
| - if (props.type === "action") { |
69 |
| - if (colorMode.value == "dark") { |
70 |
| - eventColor = "#DD7E6B"; |
71 |
| - } else { |
72 |
| - eventColor = "#9A031E"; |
73 |
| - } |
74 |
| - } else { |
75 |
| - if (colorMode.value == "dark") { |
76 |
| - eventColor = "#6D9EEB"; |
77 |
| - } else { |
78 |
| - eventColor = "#006DAA"; |
79 |
| - } |
80 |
| - } |
81 |
| -
|
82 |
| - const markerHTMLStyles = ` |
83 |
| - background-color: ${eventColor}; |
84 |
| - width: 2rem; |
85 |
| - height: 2rem; |
86 |
| - display: block; |
87 |
| - left: -1rem; |
88 |
| - top: -1.5rem; |
89 |
| - position: relative; |
90 |
| - border-radius: 2rem 2rem 0; |
91 |
| - transform: rotate(45deg); |
92 |
| - border: 1px solid #D8DEE4`; |
93 |
| -
|
94 |
| - const mapIcon = L.divIcon({ |
95 |
| - className: "my-custom-pin", |
96 |
| - iconAnchor: [0, 24], |
97 |
| - popupAnchor: [0, -36], |
98 |
| - html: `<span style="${markerHTMLStyles}" />`, |
99 |
| - }); |
100 |
| -
|
101 |
| - markers.map((marker: Marker) => { |
102 |
| - const pin = L.marker([marker.lat, marker.lon], { icon: mapIcon }); |
103 |
| - // Add location pin to map. |
104 |
| - pin.addTo(leafletMap); |
105 |
| - pin.on("click", function () { |
106 |
| - L.popup() |
107 |
| - .setLatLng(pin.getLatLng()) |
108 |
| - .setContent( |
109 |
| - ` |
110 |
| - <div class="flex bg-[#F6F8FA] rounded-lg"> |
111 |
| - <div class="flex flex-col w-3/5 px-2 pt-1 pb-2 space-y-1"> |
112 |
| - <p class="text-sm font-bold">${props.title}</p> |
113 |
| - <p class="text-xs font-semibold">Date and time</p> |
114 |
| - <p class="text-xs font-semibold">${marker.address}</p> |
115 |
| - <a href="/home" class="attend-btn py-[0.5rem] px-[1.125rem] bg-[#F1993D] text-[#F6F8FA] font-medium rounded-md w-fit"> |
116 |
| - Attend |
117 |
| - </a> |
118 |
| - </div> |
119 |
| - <div class="w-2/5 border-l-[24px] border-[#9A031E] bg-[#898688] rounded-r-md"> |
120 |
| - <img src=""/> |
121 |
| - </div> |
122 |
| - </div> |
123 |
| - ` |
124 |
| - ) |
125 |
| - .openOn(leafletMap); |
126 |
| - }); |
127 |
| - }); |
128 |
| -} |
129 |
| -
|
130 |
| -/* |
131 |
| - NOTE: Below is an example of the code to use when the backend code |
132 |
| - is up and running, removing most of the logic from the frontend. |
133 |
| -
|
134 |
| - const props = defineProps<{ |
135 |
| - locations: Marker[], |
136 |
| - averageLat: number, |
137 |
| - averageLon: number |
138 |
| - }>(); |
139 |
| -
|
140 |
| - onMounted(() => { |
141 |
| - drawMap(props.averageLat, props.averageLon, props.locations); |
142 |
| - }); |
143 |
| -*/ |
144 |
| -
|
145 |
| -onMounted(() => { |
146 |
| - const markers: Marker[] = []; |
147 |
| - let averageLat: number = 0; |
148 |
| - let averageLon: number = 0; |
149 |
| -
|
150 |
| - props.addresses.forEach((address: string, index: number) => { |
151 |
| - const formattedAddress = address.replace(/ /g, "+"); // replace spaces with + |
152 |
| -
|
153 |
| - const osmURL = |
154 |
| - "https://nominatim.openstreetmap.org/search?q=" + |
155 |
| - formattedAddress + |
156 |
| - "&format=json"; |
157 |
| -
|
158 |
| - fetch(osmURL) // get the latitude and longitude of the address |
159 |
| - .then((response) => response.json()) |
160 |
| - .then((data) => { |
161 |
| - if (data.length == 0) { |
162 |
| - handleMapError(new Error("OSM: Provided address not found.")); |
163 |
| - return; |
164 |
| - } |
165 |
| -
|
166 |
| - const latitude = data[0].lat; |
167 |
| - const longitude = data[0].lon; |
168 |
| -
|
169 |
| - markers[index] = { address: address, lat: latitude, lon: longitude }; |
170 |
| - averageLat += +latitude; |
171 |
| - averageLon += +longitude; |
172 |
| -
|
173 |
| - if (index == props.addresses.length - 1) { |
174 |
| - // Calculate averages for centerpoint of map. |
175 |
| - averageLat /= props.addresses.length; |
176 |
| - averageLon /= props.addresses.length; |
177 |
| - drawMap(averageLat, averageLon, markers); |
178 |
| - } |
179 |
| - }) |
180 |
| - .catch((error: Error) => { |
181 |
| - handleMapError(error); |
182 |
| - }); |
183 |
| - }); |
184 |
| -}); |
185 |
| -</script> |
186 |
| - |
187 |
| -<style> |
188 |
| -.leaflet-container a.leaflet-popup-close-button { |
189 |
| - color: #f6f8fa; |
190 |
| -} |
191 |
| -
|
192 |
| -.leaflet-container a.attend-btn { |
193 |
| - color: #f6f8fa; |
194 |
| -} |
195 |
| -
|
196 |
| -.leaflet-container p { |
197 |
| - margin-top: 0.5rem; |
198 |
| - margin-bottom: 0.5rem; |
199 |
| -} |
200 |
| -
|
201 |
| -.leaflet-popup-content-wrapper { |
202 |
| - border-radius: 5px; |
203 |
| -} |
204 |
| -
|
205 |
| -.leaflet-popup-content { |
206 |
| - margin: 0rem; |
207 |
| - width: 100%; |
208 |
| - height: 100%; |
209 |
| -} |
210 |
| -</style> |
0 commit comments