2 import { untrack } from 'svelte';
3 import { update_html_title } from '../lib/api_utils.js';
4 import { appState } from '../state/AppState.svelte.js';
7 coverageType, isAdminBoundary,
8 formatAddressRank, formatKeywordToken, formatOSMType
9 } from '../lib/helpers.js';
10 import Header from '../components/Header.svelte';
11 import MapIcon from '../components/MapIcon.svelte';
12 import SearchSectionDetails from '../components/SearchSectionDetails.svelte';
13 import DetailsTableHeader from '../components/DetailsTableHeader.svelte';
14 import DetailsOneRow from '../components/DetailsOneRow.svelte';
15 import DetailsLink from '../components/DetailsLink.svelte';
16 import DetailsPostcodeHint from '../components/DetailsPostcodeHint.svelte';
17 import InfoRowList from '../components/DetailsInfoRowList.svelte';
18 import WikipediaLink from '../components/WikipediaLink.svelte';
19 import OsmLink from '../components/OsmLink.svelte';
20 import Map from '../components/Map.svelte';
22 let aPlace = $state();
23 let base_url = $state();
24 let api_request_params = $state.raw();
25 let api_request_finished = $state(false);
27 function loaddata(search_params) {
28 api_request_params = {
29 place_id: search_params.get('place_id'),
30 osmtype: search_params.get('osmtype'),
31 osmid: search_params.get('osmid'),
32 class: search_params.get('class'),
33 keywords: search_params.get('keywords'),
36 hierarchy: (search_params.get('hierarchy') === '1' ? 1 : 0),
41 api_request_finished = false;
43 if (api_request_params.place_id || (api_request_params.osmtype && api_request_params.osmid)) {
45 if (api_request_params.place_id) {
46 update_html_title('Details for ' + api_request_params.place_id);
48 update_html_title('Details for ' + api_request_params.osmtype + api_request_params.osmid);
51 appState.fetchFromApi('details', api_request_params, function (data) {
52 window.scrollTo(0, 0);
53 api_request_finished = true;
54 aPlace = (data && !data.error) ? data : undefined;
61 function place_has_keywords(aThisPlace) {
62 // Return false if Nominatim API sends 'keywords: { name: [], address: [] }'
63 // Like no longer needed after Nominatim version 4.3
65 aThisPlace.keywords && aThisPlace.keywords.name && aThisPlace.keywords.address
66 && (aThisPlace.keywords.name.length > 0 || aThisPlace.keywords.address.length > 0)
70 function country_code(aThisPlace) {
71 let aLine = aThisPlace.address.find((address_line) => address_line.type === 'country_code');
72 return aLine ? aLine.localname : null;
76 if (appState.page.tab === 'details') {
77 const params = appState.page.params;
80 base_url = window.location.search;
85 const reverse_only = Nominatim_Config.Reverse_Only;
88 {#snippet subheader()}
89 <SearchSectionDetails api_request_params={api_request_params}/>
91 <Header {subheader} />
93 <div class="container">
96 <div class="col-sm-10">
98 {aPlace.localname || `${formatOSMType(aPlace.osm_type)} ${aPlace.osm_id}` }
99 <small><DetailsLink feature={aPlace} text="link to this page" /></small>
102 <div class="col-sm-2 text-end">
103 <MapIcon aPlace={aPlace} />
107 <div class="col-md-6">
108 <table id="locationdetails" class="table table-striped table-responsive">
110 <tr><td>Name</td><td>
111 {#if aPlace.names && typeof (aPlace.names) === 'object'
112 && Object.keys(aPlace.names).length}
113 <InfoRowList items={aPlace.names} />
115 <span class="noname fw-bold">No Name</span>
118 <tr><td>Type</td><td>{aPlace.category}:{aPlace.type}</td></tr>
119 <tr><td>Last Updated</td><td>{aPlace.indexed_date}</td></tr>
120 {#if (isAdminBoundary(aPlace)) }
121 <tr><td>Admin Level</td><td>{aPlace.admin_level}</td></tr>
123 <tr><td>Search Rank</td><td>{aPlace.rank_search}</td></tr>
124 <tr><td>Address Rank</td><td>
125 {aPlace.rank_address} ({formatAddressRank(aPlace.rank_address)})
127 {#if aPlace.calculated_importance}
128 <tr><td>Importance</td><td>
129 {aPlace.calculated_importance}
130 {#if !aPlace.importance} (estimated){/if}
133 <tr><td>Coverage</td><td>{coverageType(aPlace)}</td></tr>
134 <tr><td>Centre Point (lat,lon)</td><td>
135 {aPlace.centroid.coordinates[1]},{aPlace.centroid.coordinates[0]}
138 <OsmLink osmType={aPlace.osm_type} osmId={aPlace.osm_id}/>
140 <tr><td>Place Id</td><td>
142 (<a href="https://nominatim.org/release-docs/develop/api/Output/#place_id-is-not-a-persistent-id">
146 {#if aPlace.calculated_wikipedia}
147 <tr><td>Wikipedia Calculated</td><td>
148 <WikipediaLink wikipedia={aPlace.calculated_wikipedia} />
151 <tr><td>Computed Postcode</td><td>
152 {#if aPlace.calculated_postcode || (aPlace.type === 'postcode' || !aPlace.osm_id)}
153 {aPlace.calculated_postcode || aPlace.names.ref}
154 <DetailsPostcodeHint postcode={aPlace.calculated_postcode || aPlace.names.ref}
155 lat={aPlace.centroid.coordinates[1]}
156 lon={aPlace.centroid.coordinates[0]}
157 country_code={aPlace.country_code} />
160 <tr><td>Address Tags</td><td>
161 <InfoRowList items={aPlace.addresstags} />
163 <tr><td>Extra Tags</td><td>
164 <InfoRowList items={aPlace.extratags} />
169 <div class="col-md-6">
170 <div id="map-wrapper">
171 <Map current_result={aPlace} />
175 {:else if (window.location.search !== '' && api_request_finished)}
181 <div class="col-md-12">
185 <table id="address" class="table table-striped table-small">
186 <DetailsTableHeader />
188 {#each aPlace.address as addressLine}
189 <DetailsOneRow addressLine={addressLine}
190 bMarkUnusedLines={true}
191 bDistanceInMeters={false}
192 sCountryCode={country_code(aPlace)} />
198 <h2>Linked Places</h2>
199 {#if aPlace.linked_places}
200 <table class="table table-striped table-small">
201 <DetailsTableHeader />
203 {#each aPlace.linked_places as addressLine}
204 <DetailsOneRow addressLine={addressLine}
205 bMarkUnusedLines={true}
206 bDistanceInMeters={true} />
213 {#if aPlace.entrances && aPlace.entrances.length}
214 <table class="table table-striped table-small">
218 <th>Entrance Type</th>
224 {#each aPlace.entrances as entrance, i}
225 <tr class="all-columns">
227 <td>{entrance.type}</td>
228 <td><OsmLink osmType='N' osmId={entrance.osm_id} /></td>
229 <td><InfoRowList items={entrance.extratags || {}} /></td>
235 <p>Place does not have entrances</p>
240 {#if api_request_params.keywords}
241 {#if place_has_keywords(aPlace)}
242 <h3>Name Keywords</h3>
244 <table class="table table-striped table-small">
246 {#each aPlace.keywords.name as keyword}
248 <td>{formatKeywordToken(keyword.token)}</td>
250 <td>word id: {keyword.id}</td>
256 {#if aPlace.keywords.address}
257 <h3>Address Keywords</h3>
259 <table class="table table-striped table-small">
261 {#each aPlace.keywords.address as keyword}
263 <td>{formatKeywordToken(keyword.token)}</td>
264 <td>word id: {keyword.id || '?'}</td>
271 <p>Place has no keywords</p>
274 <a class="btn btn-outline-secondary btn-sm"
275 href="{base_url}&keywords=1">display keywords</a>
280 {#if api_request_params.hierarchy}
281 {#if aPlace.hierarchy && typeof (aPlace.hierarchy) === 'object'
282 && Object.keys(aPlace.hierarchy).length}
284 {#each Object.keys(aPlace.hierarchy) as type}
286 <table class="table table-striped table-small">
287 <DetailsTableHeader />
289 {#each aPlace.hierarchy[type] as line}
290 <DetailsOneRow addressLine={line} bDistanceInMeters={true} />
295 {#if Object.keys(aPlace.hierarchy) > 500}
296 <p>There are more child objects which are not shown.</p>
299 <p>Place is not parent of other places</p>
302 <a class="btn btn-outline-secondary btn-sm"
303 href="{base_url}&hierarchy=1">display child places</a>
318 h1 small :global(a) {
325 background-color: var(--bs-body-bg);
326 border-bottom: 2px solid silver;
328 margin-bottom: 0.5em;
334 table#locationdetails td {
340 background-color: var(--bs-body-bg) !important;
344 border-top: none !important;
345 padding-left: 0 !important;
347 :global(span.noname){
348 color: var(--bs-danger);
356 border: 1px solid #666;