2 import { fetch_from_api, update_html_title } from '../lib/api_utils.js';
3 import { page } from '../lib/stores.js';
6 osmLink, wikipediaLink, coverageType, isAdminBoundary,
7 formatAddressRank, formatKeywordToken, formatOSMType
8 } from '../lib/helpers.js';
9 import Header from '../components/Header.svelte';
10 import MapIcon from '../components/MapIcon.svelte';
11 import SearchSectionDetails from '../components/SearchSectionDetails.svelte';
12 import DetailsOneRow from '../components/DetailsOneRow.svelte';
13 import DetailsLink from '../components/DetailsLink.svelte';
14 import DetailsPostcodeHint from '../components/DetailsPostcodeHint.svelte';
15 import InfoRowList from '../components/DetailsInfoRowList.svelte';
16 import Map from '../components/Map.svelte';
18 let aPlace = $state();
19 let base_url = $state();
20 let api_request_params = $state();
21 let api_request_finished = $state(false);
23 function loaddata(search_params) {
24 api_request_params = {
25 place_id: search_params.get('place_id'),
26 osmtype: search_params.get('osmtype'),
27 osmid: search_params.get('osmid'),
28 class: search_params.get('class'),
29 keywords: search_params.get('keywords'),
31 hierarchy: (search_params.get('hierarchy') === '1' ? 1 : 0),
36 api_request_finished = false;
38 if (api_request_params.place_id || (api_request_params.osmtype && api_request_params.osmid)) {
40 if (api_request_params.place_id) {
41 update_html_title('Details for ' + api_request_params.place_id);
43 update_html_title('Details for ' + api_request_params.osmtype + api_request_params.osmid);
46 fetch_from_api('details', api_request_params, function (data) {
47 window.scrollTo(0, 0);
48 api_request_finished = true;
49 aPlace = (data && !data.error) ? data : undefined;
56 function place_has_keywords(aThisPlace) {
57 // Return false if Nominatim API sends 'keywords: { name: [], address: [] }'
58 // Like no longer needed after Nominatim version 4.3
60 aThisPlace.keywords && aThisPlace.keywords.name && aThisPlace.keywords.address
61 && (aThisPlace.keywords.name.length > 0 || aThisPlace.keywords.address.length > 0)
65 function country_code(aThisPlace) {
66 let aLine = aThisPlace.address.find((address_line) => address_line.type === 'country_code');
67 return aLine ? aLine.localname : null;
70 page.subscribe((pageinfo) => {
71 if (pageinfo.tab === 'details') {
72 loaddata(pageinfo.params);
73 base_url = window.location.search;
77 const reverse_only = Nominatim_Config.Reverse_Only;
80 {#snippet subheader()}
81 <SearchSectionDetails api_request_params={api_request_params}/>
83 <Header {subheader} />
85 <div class="container">
88 <div class="col-sm-10">
90 {aPlace.localname || `${formatOSMType(aPlace.osm_type)} ${aPlace.osm_id}` }
91 <small><DetailsLink feature={aPlace} text="link to this page" /></small>
94 <div class="col-sm-2 text-end">
95 <MapIcon aPlace={aPlace} />
99 <div class="col-md-6">
100 <table id="locationdetails" class="table table-striped table-responsive">
102 <tr class="info-row"><td>Name</td><td>
103 {#if aPlace.names && typeof (aPlace.names) === 'object'
104 && Object.keys(aPlace.names).length}
105 <InfoRowList items={aPlace.names} />
107 <span class="noname fw-bold">No Name</span>
110 <tr class="info-row"><td>Type</td><td>{aPlace.category}:{aPlace.type}</td></tr>
111 <tr class="info-row"><td>Last Updated</td><td>{aPlace.indexed_date}</td></tr>
112 {#if (isAdminBoundary(aPlace)) }
113 <tr class="info-row"><td>Admin Level</td><td>{aPlace.admin_level}</td></tr>
115 <tr class="info-row"><td>Search Rank</td><td>{aPlace.rank_search}</td></tr>
116 <tr class="info-row"><td>Address Rank</td><td>
117 {aPlace.rank_address} ({formatAddressRank(aPlace.rank_address)})
119 {#if aPlace.calculated_importance}
120 <tr class="info-row"><td>Importance</td><td>
121 {aPlace.calculated_importance}
122 {#if !aPlace.importance} (estimated){/if}
125 <tr class="info-row"><td>Coverage</td><td>{coverageType(aPlace)}</td></tr>
126 <tr class="info-row"><td>Centre Point (lat,lon)</td><td>
127 {aPlace.centroid.coordinates[1]},{aPlace.centroid.coordinates[0]}
129 <!-- eslint-disable-next-line svelte/no-at-html-tags -->
130 <tr class="info-row"><td>OSM</td><td>{@html osmLink(aPlace)}</td></tr>
131 <tr class="info-row"><td>Place Id</td><td>
133 (<a href="https://nominatim.org/release-docs/develop/api/Output/#place_id-is-not-a-persistent-id">
137 {#if aPlace.calculated_wikipedia}
138 <tr class="info-row"><td>Wikipedia Calculated</td><td>
139 <!-- eslint-disable-next-line svelte/no-at-html-tags -->
140 {@html wikipediaLink(aPlace)}
143 <tr class="info-row"><td>Computed Postcode</td><td>
144 {#if aPlace.calculated_postcode}
145 {aPlace.calculated_postcode}
146 <DetailsPostcodeHint postcode={aPlace.calculated_postcode}
147 lat={aPlace.centroid.coordinates[1]}
148 lon={aPlace.centroid.coordinates[0]} />
151 <tr class="info-row"><td>Address Tags</td><td>
152 <InfoRowList items={aPlace.addresstags} />
154 <tr class="info-row"><td>Extra Tags</td><td>
155 <InfoRowList items={aPlace.extratags} />
160 <div class="col-md-6">
161 <div id="map-wrapper">
162 <Map current_result={aPlace} />
167 <div class="col-md-12">
169 <table id="address" class="table table-striped table-small">
175 <th>Address rank</th>
183 {#each aPlace.address as addressLine}
184 <DetailsOneRow addressLine={addressLine}
185 bMarkUnusedLines={true}
186 bDistanceInMeters={false}
187 sCountryCode={country_code(aPlace)} />
191 {#if aPlace.linked_places}
192 <tr class="all-columns"><td colspan="7"><h2>Linked Places</h2></td></tr>
193 {#each aPlace.linked_places as addressLine}
194 <DetailsOneRow addressLine={addressLine}
195 bMarkUnusedLines={true}
196 bDistanceInMeters={true} />
201 <tr class="all-columns"><td colspan="7"><h2>Keywords</h2></td></tr>
202 {#if api_request_params.keywords}
204 {#if place_has_keywords(aPlace)}
205 <tr class="all-columns"><td colspan="7"><h3>Name Keywords</h3></td></tr>
206 {#each aPlace.keywords.name as keyword}
208 <td>{formatKeywordToken(keyword.token)}</td>
210 <td>word id: {keyword.id}</td>
215 {#if aPlace.keywords.address}
216 <tr class="all-columns"><td colspan="7"><h3>Address Keywords</h3></td></tr>
217 {#each aPlace.keywords.address as keyword}
219 <td>{formatKeywordToken(keyword.token)}</td>
221 <td>word id: {keyword.id}</td>
227 <tr><td>Place has no keywords</td></tr>
232 <a class="btn btn-outline-secondary btn-sm"
233 href="{base_url}&keywords=1">display keywords</a>
239 <tr class="all-columns"><td colspan="7"><h2>Parent Of</h2></td></tr>
240 {#if api_request_params.hierarchy}
241 {#if aPlace.hierarchy && typeof (aPlace.hierarchy) === 'object'
242 && Object.keys(aPlace.hierarchy).length}
243 {#each Object.keys(aPlace.hierarchy) as type}
244 <tr class="all-columns"><td colspan="7"><h3>{type}</h3></td></tr>
245 {#each aPlace.hierarchy[type] as line}
246 <DetailsOneRow addressLine={line} bDistanceInMeters={true} />
250 {#if Object.keys(aPlace.hierarchy) > 500}
251 <tr><td><p>There are more child objects which are not shown.</p></td></tr>
254 <tr><td>Place is not parent of other places</td></tr>
259 <a class="btn btn-outline-secondary btn-sm"
260 href="{base_url}&hierarchy=1">display child places</a>
268 {:else if (window.location.search !== '' && api_request_finished)}
281 h1 small :global(a) {
289 background-color: var(--bs-body-bg);
297 background-color: var(--bs-body-bg) !important;
301 border-top: none !important;
302 padding-left: 0 !important;
304 :global(span.noname){
305 color: var(--bs-danger);
318 border: 1px solid #666;