Skip to content

Commit a98d089

Browse files
committed
feat: add address search to public map
1 parent f36745c commit a98d089

File tree

1 file changed

+96
-2
lines changed

1 file changed

+96
-2
lines changed

ckanext/spatial/public/js/spatial_query.js

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,18 @@ this.ckan.module('spatial-query', function ($, _) {
5454
'<h4 class="modal-title"></h4>',
5555
'<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>',
5656
'</div>',
57-
'<div class="modal-body"><div id="draw-map-container"></div></div>',
57+
`<div class="modal-body">
58+
<input class="rounded-2" id="search-address-box" type="text" placeholder="Search for an address.">
59+
<button class="btn btn-primary" type="button" id="search-address-button" disabled>Search address</button>
60+
<div id="search-dropdown" class="dropdown d-inline-flex d-none" style="width: fit-content">
61+
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenu2" data-bs-toggle="dropdown" aria-expanded="false">
62+
View results
63+
</button>
64+
<ul id="search-dropdown-list" class="dropdown-menu" style="z-index: 1001;" aria-labelledby="dropdownMenu2"></ul>
65+
</div>
66+
<span id="no-results-text" class="d-none text-danger">No results found.</span>
67+
<div id="draw-map-container">
68+
</div></div>`,
5869
'<div class="modal-footer">',
5970
'<button type="button" class="btn btn-secondary btn-cancel" data-bs-dismiss="modal"></button>',
6071
'<button type="button" class="btn btn-primary apply disabled"></button>',
@@ -83,6 +94,54 @@ this.ckan.module('spatial-query', function ($, _) {
8394
this.el.ready(this._onReady);
8495
},
8596

97+
async runAddressSearch(search_query) {
98+
this.searchAddressButton.innerText = "Searching...";
99+
const nominatimEndpoint = `https://nominatim.openstreetmap.org/search?addressdetails=1&q=${search_query}&format=jsonv2&limit=10`;
100+
fetch(nominatimEndpoint, {
101+
headers: {
102+
"User-Agent": "Texas Water Development Hub"
103+
},
104+
signal: AbortSignal.timeout(5000)
105+
}).then((res) => res.json().then((data) => {
106+
if (data && data.length > 1) {
107+
this.searchResults = data.map((entry) => { return { "display_name": entry.display_name, "boundingbox": entry.boundingbox }; })
108+
// Jump to first result.
109+
const firstBoundingBox = this.searchResults[0]["boundingbox"];
110+
this.drawMap.fitBounds([[firstBoundingBox[0], firstBoundingBox[2]], [firstBoundingBox[1], firstBoundingBox[3]]]);
111+
const searchDropdownList = document.getElementById("search-dropdown-list");
112+
// Remove previous search results
113+
while (searchDropdownList.hasChildNodes()) {
114+
searchDropdownList.removeChild(searchDropdownList.firstChild)
115+
}
116+
// Add search results to the dropdown
117+
for (const entry of this.searchResults) {
118+
const entryLi = document.createElement("li");
119+
const entryButton = document.createElement("button");
120+
entryButton.classList.add("dropdown-item");
121+
entryButton.type = "button";
122+
// entryButton.innerText = `${entry["display_name"]} | (${entry["lat"]}, ${entry["lon"]})`;
123+
entryButton.innerText = entry["display_name"];
124+
entryButton.onclick = () => {
125+
const boundingbox = entry["boundingbox"];
126+
this.drawMap.fitBounds([[boundingbox[0], boundingbox[2]], [boundingbox[1], boundingbox[3]]]);
127+
}
128+
entryLi.appendChild(entryButton);
129+
searchDropdownList.appendChild(entryLi);
130+
};
131+
this.searchDropdown.classList.remove("d-none");
132+
}
133+
else if (data && data.length > 0) {
134+
const boundingBox = data[0]["boundingbox"];
135+
this.drawMap.fitBounds([[boundingBox[0], boundingBox[2]], [boundingBox[1], boundingBox[3]]]);
136+
} else {
137+
this.noResultsText.classList.remove("d-none");
138+
}
139+
})).finally(() => {
140+
this.searchAddressButton.removeAttribute("disabled")
141+
this.searchAddressButton.innerText = "Search address";
142+
});
143+
},
144+
86145
_getBootstrapVersion: function () {
87146
return $.fn.modal.Constructor.VERSION.split(".")[0];
88147
},
@@ -95,7 +154,7 @@ this.ckan.module('spatial-query', function ($, _) {
95154
element.modal({show: false});
96155

97156
element.find('.modal-title').text(this._('Please draw query extent in the map:'));
98-
element.find('.btn-primary').text(this._('Apply'));
157+
element.find('.apply').text(this._('Apply'));
99158
element.find('.btn-cancel').text(this._('Cancel'));
100159

101160
var module = this;
@@ -145,6 +204,41 @@ this.ckan.module('spatial-query', function ($, _) {
145204

146205
$('a.leaflet-draw-draw-rectangle>span', element).trigger('click');
147206
element.find('.btn-primary').focus()
207+
208+
// Search address feature
209+
module.searchAddressBox = document.getElementById('search-address-box');
210+
module.searchAddressButton = document.getElementById('search-address-button');
211+
module.searchDropdown = document.getElementById("search-dropdown");
212+
module.noResultsText = document.getElementById("no-results-text");
213+
// Disable default enter key behavior when pressing enter in the searchbox
214+
module.searchAddressBox.onkeydown = (e) => {
215+
module.noResultsText.classList.add("d-none");
216+
module.searchDropdown.classList.add("d-none");
217+
if (e.key === "Enter" && (!module.searchAddressButton.getAttribute("disabled") || module.searchAddressButton.getAttribute("disabled") === "false")) {
218+
e?.preventDefault();
219+
}
220+
}
221+
module.searchAddressBox.onkeyup = (e) => {
222+
e?.preventDefault();
223+
// When there is a value in the searchbox, enable the search button
224+
if (module.searchAddressBox.value) {
225+
module.searchAddressButton.removeAttribute("disabled")
226+
}
227+
// When the searchbox is empty, disable the search button
228+
else {
229+
module.searchAddressButton.setAttribute("disabled", true)
230+
}
231+
// If the user presses the Enter key in the searchbox and the search button is not disabled, run the search
232+
if (e.key === "Enter" && (!module.searchAddressButton.getAttribute("disabled") || module.searchAddressButton.getAttribute("disabled") === "false")) {
233+
module.searchAddressButton.click();
234+
}
235+
}
236+
// If the search button is clicked, disable the search button and run the search
237+
module.searchAddressButton.onclick = (e) => {
238+
e?.preventDefault();
239+
module.searchAddressButton.setAttribute("disabled", true);
240+
module.runAddressSearch(module.searchAddressBox.value);
241+
}
148242
})
149243

150244
this.modal.on('hidden.bs.modal', function () {

0 commit comments

Comments
 (0)