Skip to content

Commit 8ec25b8

Browse files
authored
Merge pull request #34 from OpenWebconcept/feature/OWC-94
OWC-94 add search by street place or zipcode
2 parents 39f926a + 9ca2ca2 commit 8ec25b8

6 files changed

Lines changed: 95 additions & 172 deletions

File tree

build/blocks/owc-openkaarten/streetmap/client.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/blocks/owc-openkaarten/streetmap/client.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/mix-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"/blocks/owc-openkaarten/streetmap/client.js": "/blocks/owc-openkaarten/streetmap/client.js?id=3c8f46a7300885cb005671988f825e18",
2+
"/blocks/owc-openkaarten/streetmap/client.js": "/blocks/owc-openkaarten/streetmap/client.js?id=84c72006b52ad16cac76b6fb48989dac",
33
"/blocks/owc-openkaarten/streetmap/style.css": "/blocks/owc-openkaarten/streetmap/style.css?id=0c70cc29722d11466724176fc03cdd58",
44
"/blocks/owc-openkaarten/streetmap/editor.css": "/blocks/owc-openkaarten/streetmap/editor.css?id=f4316f0723fb86e3b028a9b448b1f3ae"
55
}

src/blocks/owc-openkaarten/streetmap/assets/scripts/vue/BaseSearchInput.vue

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,82 +10,79 @@ const props = defineProps({
1010
type: String,
1111
required: true,
1212
},
13+
resultsCount: {
14+
type: Number,
15+
default: 0
16+
},
1317
});
1418
1519
const emit = defineEmits(['search']);
1620
const searchQuery = ref('');
17-
const resultsCount = ref(0);
1821
1922
const handleSubmit = (e) => {
2023
e.preventDefault();
2124
emit('search', searchQuery.value);
2225
};
2326
24-
const handleInput = () => {
25-
emit('search', searchQuery.value);
26-
};
27-
2827
const clearSearch = () => {
2928
searchQuery.value = '';
3029
emit('search', '');
3130
};
3231
3332
const searchStatus = computed(() => {
3433
if (!searchQuery.value) return '';
35-
return `${resultsCount.value} resultaten gevonden voor "${searchQuery.value}"`;
34+
return `${props.resultsCount} resultaten gevonden voor "${searchQuery.value}"`;
3635
});
36+
3737
</script>
3838

3939
<template>
4040
<search class="search-container">
41-
<form
42-
@submit="handleSubmit"
41+
<form
42+
@submit="handleSubmit"
4343
class="search-form"
4444
:style="{ '--search-primary-color': primaryColor }"
4545
>
46-
<label class="sr-only" for="location-search">Zoeken op locatie</label>
46+
<label class="sr-only" for="location-search">Zoek op straat en/of plaats of postcode</label>
4747
<div class="search-wrapper">
48-
<div
48+
<div
4949
role="status"
5050
aria-live="polite"
5151
aria-atomic="true"
5252
class="sr-only"
5353
>
5454
<span>{{ searchStatus }}</span>
5555
</div>
56-
5756
<input
5857
id="location-search"
5958
type="search"
6059
v-model="searchQuery"
6160
:placeholder="placeholder"
62-
@input="handleInput"
63-
autocomplete="off"
6461
class="search-input"
65-
/>
62+
/>
6663

67-
<button
64+
<button
6865
v-if="searchQuery"
6966
type="button"
7067
class="search-clear"
7168
@click="clearSearch"
7269
aria-label="Zoekopdracht wissen"
73-
>
74-
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
75-
<path d="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" />
76-
</svg>
77-
</button>
78-
79-
<button
80-
type="submit"
70+
>
71+
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
72+
<path d="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" />
73+
</svg>
74+
</button>
75+
76+
<button
77+
type="submit"
8178
class="search-submit"
8279
:disabled="!searchQuery"
8380
aria-label="Zoeken"
84-
>
85-
<svg aria-hidden="true" focusable="false" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
86-
<path d="M15.5 14H14.71L14.43 13.73C15.41 12.59 16 11.11 16 9.5C16 5.91 13.09 3 9.5 3C5.91 3 3 5.91 3 9.5C3 13.09 5.91 16 9.5 16C11.11 16 12.59 15.41 13.73 14.43L14 14.71V15.5L19 20.49L20.49 19L15.5 14ZM9.5 14C7.01 14 5 11.99 5 9.5C5 7.01 7.01 5 9.5 5C11.99 5 14 7.01 14 9.5C14 11.99 11.99 14 9.5 14Z" fill="currentColor"/>
87-
</svg>
88-
</button>
81+
>
82+
<svg aria-hidden="true" focusable="false" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
83+
<path d="M15.5 14H14.71L14.43 13.73C15.41 12.59 16 11.11 16 9.5C16 5.91 13.09 3 9.5 3C5.91 3 3 5.91 3 9.5C3 13.09 5.91 16 9.5 16C11.11 16 12.59 15.41 13.73 14.43L14 14.71V15.5L19 20.49L20.49 19L15.5 14ZM9.5 14C7.01 14 5 11.99 5 9.5C5 7.01 7.01 5 9.5 5C11.99 5 14 7.01 14 9.5C14 11.99 11.99 14 9.5 14Z" fill="currentColor"/>
84+
</svg>
85+
</button>
8986
</div>
9087
</form>
9188
</search>
@@ -132,6 +129,26 @@ const searchStatus = computed(() => {
132129
outline: 2px solid var(--search-primary-color);
133130
outline-offset: 2px;
134131
}
132+
133+
input[type="search"]::-ms-clear {
134+
display: none;
135+
width: 0;
136+
height: 0;
137+
}
138+
139+
&::-ms-reveal {
140+
display: none;
141+
width: 0;
142+
height: 0;
143+
}
144+
145+
/* clears the 'X' in searchbar from Chrome */
146+
&::-webkit-search-decoration,
147+
&::-webkit-search-cancel-button,
148+
&::-webkit-search-results-button,
149+
&::-webkit-search-results-decoration {
150+
display: none;
151+
}
135152
}
136153
137154
.search-clear,
@@ -167,4 +184,4 @@ const searchStatus = computed(() => {
167184
opacity: 0.8;
168185
}
169186
}
170-
</style>
187+
</style>

src/blocks/owc-openkaarten/streetmap/assets/scripts/vue/ListView.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ const handleSearch = (query) => {
128128
<div class="list-view__controls">
129129
<BaseSearchInput
130130
:primary-color="primaryColor"
131+
:results-count="filteredLocations.length"
131132
@search="handleSearch"
132133
/>
133134
<button @click="toggleView" class="list-view__map-button" v-html="mapButtonHTML"></button>

src/blocks/owc-openkaarten/streetmap/assets/scripts/vue/TheMap.vue

Lines changed: 45 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,11 @@ const props = defineProps({
3636
3737
const tooltipCard = ref(null);
3838
const showFiltersCard = ref(false);
39-
const datasetLocations = ref([]);
4039
const mapRef = ref(null);
4140
const clusters = ref([]);
42-
const showListView = ref(false);
4341
const searchQuery = ref('');
44-
const searchMarkers = ref([]);
42+
const resultsCount = ref(0);
43+
const lastSearchMarker = ref(null);
4544
const clusterOptions = {
4645
spiderfyOnMaxZoom: true,
4746
showCoverageOnHover: false,
@@ -64,8 +63,6 @@ const clusterOptions = {
6463
},
6564
};
6665
67-
const locationAddresses = ref(new Map());
68-
6966
const closeTooltipCard = () => {
7067
tooltipCard.value = null;
7168
document.getElementById('dataset-map')?.focus();
@@ -104,12 +101,6 @@ const attachEvents = (marker, location, set) => {
104101
});
105102
};
106103
107-
// Helper function to get coordinates from feature
108-
const getLatLng = (feature) => {
109-
const coords = feature.geometry.coordinates;
110-
return L.latLng(coords[1], coords[0]);
111-
};
112-
113104
// Helper function to get color from marker config
114105
const getColorFromMarker = (markerConfig) => {
115106
if (!markerConfig) return props.primaryColor;
@@ -174,8 +165,8 @@ const initializeMap = (datasets) => {
174165
centerX: lat,
175166
centerY: long,
176167
minimumZoom: 4,
177-
maximumZoom: 16,
178-
defaultZoom: 13,
168+
maximumZoom: 18,
169+
defaultZoom: 10,
179170
enableHomeControl: true,
180171
enableZoomControl: true,
181172
enableBoxZoomControl: true,
@@ -293,133 +284,46 @@ onMounted(async () => {
293284
}
294285
});
295286
296-
const filteredLocations = computed(() => {
297-
if (!searchQuery.value) return [];
298-
299-
const query = searchQuery.value.toLowerCase();
300-
return props.datasets
301-
.filter(dataset => props.selectedDatasets.includes(dataset.id))
302-
.flatMap(dataset => dataset.features.map(feature => {
303-
const tooltipData = feature.properties?.tooltip || [];
304-
const coords = feature.geometry.coordinates;
305-
306-
// Collect all searchable text
307-
const searchableText = [
308-
// Tooltip data
309-
tooltipData.find(t => t.layout === 'meta')?.meta,
310-
tooltipData.find(t => t.layout === 'title')?.title,
311-
tooltipData.find(t => t.layout === 'text')?.text,
312-
// Location data
313-
feature.properties?.name,
314-
feature.properties?.description,
315-
// Coordinates (formatted for search)
316-
coords ? `${coords[1]},${coords[0]}` : null
317-
]
318-
.filter(Boolean) // Remove null/undefined values
319-
.join(' ')
320-
.toLowerCase();
321-
322-
return {
323-
...feature,
324-
datasetId: dataset.id,
325-
matches: searchableText.includes(query)
326-
};
327-
}))
328-
.filter(location => location.matches);
329-
});
330-
331287
// Add search handler
332-
const handleSearch = (query) => {
333-
searchQuery.value = query;
334-
const map = mapRef.value;
335-
336-
if (!map) return;
337-
338-
// Clear existing search markers
339-
searchMarkers.value.forEach(marker => {
340-
if (marker && map.hasLayer(marker)) {
341-
map.removeLayer(marker);
342-
}
343-
});
344-
searchMarkers.value = [];
345-
346-
if (!query) {
347-
// Show all markers again when search is cleared
348-
clusters.value.forEach(({ cluster }) => {
349-
if (cluster && !map.hasLayer(cluster)) {
350-
map.addLayer(cluster);
351-
}
352-
});
353-
return;
354-
}
355-
356-
// Hide all regular markers
357-
clusters.value.forEach(({ cluster }) => {
358-
if (cluster && map.hasLayer(cluster)) {
359-
map.removeLayer(cluster);
360-
}
361-
});
362-
363-
// Create markers for search results and zoom to bounds
364-
const searchBounds = L.latLngBounds();
365-
let hasValidResults = false;
366-
367-
filteredLocations.value.forEach(location => {
368-
if (location?.geometry?.coordinates) {
369-
if (location.geometry.type === 'MultiPoint') {
370-
location.geometry.coordinates.forEach(coord => {
371-
if (Array.isArray(coord) && coord.length >= 2) {
372-
const latlng = L.latLng(coord[1], coord[0]);
373-
addSearchMarker(latlng, location);
374-
searchBounds.extend(latlng);
375-
hasValidResults = true;
376-
}
377-
});
378-
} else {
379-
const coords = location.geometry.coordinates;
380-
if (Array.isArray(coords) && coords.length >= 2) {
381-
const latlng = L.latLng(coords[1], coords[0]);
382-
addSearchMarker(latlng, location);
383-
searchBounds.extend(latlng);
384-
hasValidResults = true;
385-
}
386-
}
387-
}
388-
});
389-
390-
if (hasValidResults) {
391-
// Zoom to results with padding
392-
map.fitBounds(searchBounds, {
393-
padding: [50, 50],
394-
maxZoom: 15
395-
});
396-
}
397-
};
398-
399-
// Helper function to add search result markers
400-
const addSearchMarker = (latlng, location) => {
401-
const map = mapRef.value;
402-
if (!map || !latlng || !location) return;
403-
404-
try {
405-
const icon = makeMarkerIcon(L, {
406-
marker: location.properties?.marker,
407-
defaultColor: props.primaryColor,
408-
});
288+
const handleSearch = async (query) => {
289+
searchQuery.value = query;
290+
const map = mapRef.value;
291+
292+
if (!map || !query) return;
293+
294+
try {
295+
const response = await fetch(
296+
`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(query)}`
297+
);
298+
const results = await response.json();
299+
300+
if (results.length > 0) {
301+
const { lat, lon } = results[0]; // Take the first (most relevant) match.
302+
const latLng = L.latLng(parseFloat(lat), parseFloat(lon));
303+
const targetIcon = L.icon({
304+
iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAFgUlEQVR4Aa1XA5BjWRTN2oW17d3YaZtr2962HUzbDNpjszW24mRt28p47v7zq/bXZtrp/lWnXr337j3nPCe85NcypgSFdugCpW5YoDAMRaIMqRi6aKq5E3YqDQO3qAwjVWrD8Ncq/RBpykd8oZUb/kaJutow8r1aP9II0WmLKLIsJyv1w/kqw9Ch2MYdB++12Onxee/QMwvf4/Dk/Lfp/i4nxTXtOoQ4pW5Aj7wpici1A9erdAN2OH64x8OSP9j3Ft3b7aWkTg/Fm91siTra0f9on5sQr9INejH6CUUUpavjFNq1B+Oadhxmnfa8RfEmN8VNAsQhPqF55xHkMzz3jSmChWU6f7/XZKNH+9+hBLOHYozuKQPxyMPUKkrX/K0uWnfFaJGS1QPRtZsOPtr3NsW0uyh6NNCOkU3Yz+bXbT3I8G3xE5EXLXtCXbbqwCO9zPQYPRTZ5vIDXD7U+w7rFDEoUUf7ibHIR4y6bLVPXrz8JVZEql13trxwue/uDivd3fkWRbS6/IA2bID4uk0UpF1N8qLlbBlXs4Ee7HLTfV1j54APvODnSfOWBqtKVvjgLKzF5YdEk5ewRkGlK0i33Eofffc7HT56jD7/6U+qH3Cx7SBLNntH5YIPvODnyfIXZYRVDPqgHtLs5ABHD3YzLuespb7t79FY34DjMwrVrcTuwlT55YMPvOBnRrJ4VXTdNnYug5ucHLBjEpt30701A3Ts+HEa73u6dT3FNWwflY86eMHPk+Yu+i6pzUpRrW7SNDg5JHR4KapmM5Wv2E8Tfcb1HoqqHMHU+uWDD7zg54mz5/2BSnizi9T1Dg4QQXLToGNCkb6tb1NU+QAlGr1++eADrzhn/u8Q2YZhQVlZ5+CAOtqfbhmaUCS1ezNFVm2imDbPmPng5wmz+gwh+oHDce0eUtQ6OGDIyR0uUhUsoO3vfDmmgOezH0mZN59x7MBi++WDL1g/eEiU3avlidO671bkLfwbw5XV2P8Pzo0ydy4t2/0eu33xYSOMOD8hTf4CrBtGMSoXfPLchX+J0ruSePw3LZeK0juPJbYzrhkH0io7B3k164hiGvawhOKMLkrQLyVpZg8rHFW7E2uHOL888IBPlNZ1FPzstSJM694fWr6RwpvcJK60+0HCILTBzZLFNdtAzJaohze60T8qBzyh5ZuOg5e7uwQppofEmf2++DYvmySqGBuKaicF1blQjhuHdvCIMvp8whTTfZzI7RldpwtSzL+F1+wkdZ2TBOW2gIF88PBTzD/gpeREAMEbxnJcaJHNHrpzji0gQCS6hdkEeYt9DF/2qPcEC8RM28Hwmr3sdNyht00byAut2k3gufWNtgtOEOFGUwcXWNDbdNbpgBGxEvKkOQsxivJx33iow0Vw5S6SVTrpVq11ysA2Rp7gTfPfktc6zhtXBBC+adRLshf6sG2RfHPZ5EAc4sVZ83yCN00Fk/4kggu40ZTvIEm5g24qtU4KjBrx/BTTH8ifVASAG7gKrnWxJDcU7x8X6Ecczhm3o6YicvsLXWfh3Ch1W0k8x0nXF+0fFxgt4phz8QvypiwCCFKMqXCnqXExjq10beH+UUA7+nG6mdG/Pu0f3LgFcGrl2s0kNNjpmoJ9o4B29CMO8dMT4Q5ox8uitF6fqsrJOr8qnwNbRzv6hSnG5wP+64C7h9lp30hKNtKdWjtdkbuPA19nJ7Tz3zR/ibgARbhb4AlhavcBebmTHcFl2fvYEnW0ox9xMxKBS8btJ+KiEbq9zA4RthQXDhPa0T9TEe69gWupwc6uBUphquXgf+/FrIjweHQS4/pduMe5ERUMHUd9xv8ZR98CxkS4F2n3EUrUZ10EYNw7BWm9x1GiPssi3GgiGRDKWRYZfXlON+dfNbM+GgIwYdwAAAAASUVORK5CYII=',
305+
iconSize: [15, 24],
306+
iconAnchor: [0, 24],
307+
popupAnchor: [8, -7]
308+
});
409309
410-
const marker = new L.Marker(latlng, {
411-
icon,
412-
zIndexOffset: 1000 // Ensure search results appear above other markers
413-
});
414-
415-
if (marker) {
416-
attachEvents(marker, location, { id: location.datasetId });
417-
marker.addTo(map);
418-
searchMarkers.value.push(marker);
419-
}
420-
} catch (error) {
421-
console.error('Error adding search marker:', error);
422-
}
310+
if (map && map._loaded) {
311+
map.once('zoomend', () => {
312+
if (lastSearchMarker.value) {
313+
map.removeLayer(lastSearchMarker.value);
314+
}
315+
const targetMarker = L.marker(latLng, { icon: targetIcon });
316+
targetMarker.addTo(map);
317+
targetMarker.bindPopup("Gevonden locatie");
318+
lastSearchMarker.value = targetMarker;
319+
});
320+
map.flyTo(latLng, 15, { animate: true, duration: 1 });
321+
}
322+
resultsCount.value = results.length ? 1 : 0
323+
}
324+
} catch (error) {
325+
console.error("Error in retrieving location:", error);
326+
}
423327
};
424328
</script>
425329
@@ -432,10 +336,11 @@ const addSearchMarker = (latlng, location) => {
432336
}"
433337
>
434338
<div class="owc-openkaarten-streetmap__controls">
435-
<BaseSearchInput
339+
<BaseSearchInput
340+
:placeholder="'Zoek op straat en/of plaats of postcode'"
436341
:primary-color="primaryColor"
342+
:results-count="resultsCount"
437343
@search="handleSearch"
438-
:results-count="filteredLocations.length"
439344
/>
440345
</div>
441346
<div id="dataset-map"></div>

0 commit comments

Comments
 (0)