<script>
- import { onMount } from 'svelte';
import { fetch_from_api, update_html_title } from '../lib/api_utils.js';
- import { current_result_store } from '../lib/stores.js';
+ import { page } from '../lib/stores.js';
- import { osmLink, detailsURL, wikipediaLink, coverageType, isAdminBoundary, formatAddressRank, formatKeywordToken } from '../lib/helpers.js';
+ import {
+ osmLink, wikipediaLink, coverageType, isAdminBoundary,
+ formatAddressRank, formatKeywordToken, formatOSMType
+ } from '../lib/helpers.js';
+ import Header from '../components/Header.svelte';
import MapIcon from '../components/MapIcon.svelte';
- import DetailsIndex from '../components/DetailsIndex.svelte';
+ import SearchSectionDetails from '../components/SearchSectionDetails.svelte';
import DetailsOneRow from '../components/DetailsOneRow.svelte';
+ import DetailsLink from '../components/DetailsLink.svelte';
+ import DetailsPostcodeHint from '../components/DetailsPostcodeHint.svelte';
+ import InfoRow from '../components/DetailsInfoRow.svelte';
+ import InfoRowList from '../components/DetailsInfoRowList.svelte';
import Map from '../components/Map.svelte';
let aPlace;
- let base_url = window.location.search;
+ let base_url;
+ let api_request_params;
+ let api_request_finished = false;
- function loaddata() {
-
- var search_params = new URLSearchParams(window.location.search);
-
- var api_request_params = {
+ function loaddata(search_params) {
+ api_request_params = {
place_id: search_params.get('place_id'),
osmtype: search_params.get('osmtype'),
osmid: search_params.get('osmid'),
polygon_geojson: 1,
format: 'json'
};
+ api_request_finished = false;
if (api_request_params.place_id || (api_request_params.osmtype && api_request_params.osmid)) {
update_html_title('Details for ' + api_request_params.osmtype + api_request_params.osmid);
}
- fetch_from_api('details', api_request_params, function(data){
- aPlace = data;
- current_result_store.set(data);
+ fetch_from_api('details', api_request_params, function (data) {
+ window.scrollTo(0, 0);
+ api_request_finished = true;
+ aPlace = (data && !data.error) ? data : undefined;
});
} else {
aPlace = undefined;
}
}
- onMount(loaddata);
+ function place_has_keywords(aThisPlace) {
+ // Return false if Nominatim API sends 'keywords: { name: [], address: [] }'
+ // Like no longer needed after Nominatim version 4.3
+ return (
+ aThisPlace.keywords && aThisPlace.keywords.name && aThisPlace.keywords.address
+ && (aThisPlace.keywords.name.length > 0 || aThisPlace.keywords.address.length > 0)
+ );
+ }
+
+ function country_code(aThisPlace) {
+ let aLine = aThisPlace.address.find((address_line) => address_line.type === 'country_code');
+ return aLine ? aLine.localname : null;
+ }
+
+ $: {
+ let pageinfo = $page;
+ if (pageinfo.tab === 'details') {
+ loaddata(pageinfo.params);
+ base_url = window.location.search;
+ }
+ }
+ $: reverse_only = Nominatim_Config.Reverse_Only;
</script>
-{#if aPlace}
- <div class="container">
+<Header>
+ <SearchSectionDetails api_request_params={api_request_params}/>
+</Header>
+
+<div class="container">
+ {#if aPlace}
<div class="row">
<div class="col-sm-10">
<h1>
- {aPlace.localname}
- <small><a href="{detailsURL(aPlace)}">link to this page</a></small>
+ {aPlace.localname || `${formatOSMType(aPlace.osm_type)} ${aPlace.osm_id}` }
+ <small><DetailsLink feature={aPlace}>link to this page</DetailsLink></small>
</h1>
</div>
- <div class="col-sm-2 text-right">
+ <div class="col-sm-2 text-end">
<MapIcon aPlace={aPlace} />
</div>
</div>
<div class="row">
<div class="col-md-6">
- <table id="locationdetails" class="table table-striped">
+ <table id="locationdetails" class="table table-striped table-responsive">
<tbody>
- <tr>
- <td>Name</td>
- <td>
- {#each Object.keys(aPlace.names) as name}
- <div class="line">
- <span class="name">{aPlace.names[name]}</span> ({name})
- </div>
- {/each}
- </td>
- </tr>
- <tr>
- <td>Type</td>
- <td>{aPlace.category}:{aPlace.type}</td>
- </tr>
- <tr>
- <td>Last Updated</td>
- <td>{aPlace.indexed_date}</td>
- </tr>
+ <InfoRow title="Name">
+ {#if aPlace.names && typeof (aPlace.names) === 'object'
+ && Object.keys(aPlace.names).length}
+ <InfoRowList items={aPlace.names} />
+ {:else}
+ <span class="noname fw-bold">No Name</span>
+ {/if}
+ </InfoRow>
+ <InfoRow title="Type">{aPlace.category}:{aPlace.type}</InfoRow>
+ <InfoRow title="Last Updated">{aPlace.indexed_date}</InfoRow>
{#if (isAdminBoundary(aPlace)) }
- <tr>
- <td>Admin Level</td>
- <td>{aPlace.admin_level}</td>
- </tr>
+ <InfoRow title="Admin Level">{aPlace.admin_level}</InfoRow>
{/if}
- <tr>
- <td>Search Rank</td>
- <td>{aPlace.rank_search}</td>
- </tr>
- <tr>
- <td>Address Rank</td>
- <td>{aPlace.rank_address} ({formatAddressRank(aPlace.rank_address)})</td>
- </tr>
+ <InfoRow title="Search Rank">{aPlace.rank_search}</InfoRow>
+ <InfoRow title="Address Rank">
+ {aPlace.rank_address} ({formatAddressRank(aPlace.rank_address)})
+ </InfoRow>
{#if aPlace.calculated_importance}
- <tr>
- <td>Importance</td>
- <td>
+ <InfoRow title="Importance">
{aPlace.calculated_importance}
{#if !aPlace.importance} (estimated){/if}
- </td>
- </tr>
+ </InfoRow>
{/if}
- <tr>
- <td>Coverage</td>
- <td>{coverageType(aPlace)}</td>
- </tr>
- <tr>
- <td>Centre Point (lat,lon)</td>
- <td>
+ <InfoRow title="Coverage">{coverageType(aPlace)}</InfoRow>
+ <InfoRow title="Centre Point (lat,lon)">
{aPlace.centroid.coordinates[1]},{aPlace.centroid.coordinates[0]}
- </td>
- </tr>
- <tr>
- <td>OSM</td>
- <td>{@html osmLink(aPlace)}
- </tr>
- <tr>
- <td>
- Place Id
- (<a href="https://nominatim.org/release-docs/develop/api/Output/#place_id-is-not-a-persistent-id">on this server</a>)
- </td>
- <td>{aPlace.place_id}</td>
- </tr>
+ </InfoRow>
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
+ <InfoRow title="OSM">{@html osmLink(aPlace)}</InfoRow>
+ <InfoRow title="Place Id">
+ {aPlace.place_id}
+ (<a href="https://nominatim.org/release-docs/develop/api/Output/#place_id-is-not-a-persistent-id">
+ on this server
+ </a>)
+ </InfoRow>
{#if aPlace.calculated_wikipedia}
- <tr>
- <td>Wikipedia Calculated</td>
- <td>{@html wikipediaLink(aPlace)}</td>
- </tr>
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
+ <InfoRow title="Wikipedia Calculated">{@html wikipediaLink(aPlace)}</InfoRow>
{/if}
- <tr>
- <td>Computed Postcode</td>
- <td>{aPlace.calculated_postcode}</td>
- </tr>
- <tr>
- <td>Address Tags</td>
- <td>
- {#each Object.keys(aPlace.addresstags) as name}
- <div class="line">
- <span class="name">{aPlace.addresstags[name]}</span> ({name})
- </div>
- {/each}
- </td>
- </tr>
- <tr>
- <td>Extra Tags</td>
- <td>
- {#each Object.keys(aPlace.extratags) as name}
- <div class="line">
- <span class="name">{aPlace.extratags[name]}</span> ({name})
- </div>
- {/each}
- </td>
- </tr>
+ <InfoRow title="Computed Postcode">
+ {#if aPlace.calculated_postcode}
+ {aPlace.calculated_postcode}
+ <DetailsPostcodeHint postcode={aPlace.calculated_postcode}
+ lat={aPlace.centroid.coordinates[1]}
+ lon={aPlace.centroid.coordinates[0]} />
+ {/if}
+ </InfoRow>
+ <InfoRow title="Address Tags"><InfoRowList items={aPlace.addresstags} /></InfoRow>
+ <InfoRow title="Extra Tags"><InfoRowList items={aPlace.extratags} /></InfoRow>
</tbody>
</table>
</div>
<div class="col-md-6">
<div id="map-wrapper">
- <Map/>
+ <Map current_result={aPlace} />
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h2>Address</h2>
- <table id="address" class="table table-striped table-small">
+ <table id="address" class="table table-striped table-small">
<thead>
<tr>
<th>Local name</th>
<tbody>
{#if aPlace.address}
{#each aPlace.address as addressLine}
- <DetailsOneRow addressLine={addressLine} bDistanceInMeters=false />
+ <DetailsOneRow addressLine={addressLine}
+ bMarkUnusedLines={true}
+ bDistanceInMeters={false}
+ sCountryCode={country_code(aPlace)} />
{/each}
{/if}
{#if aPlace.linked_places}
- <tr class="all-columns"><td colspan="6"><h2>Linked Places</h2></td></tr>
+ <tr class="all-columns"><td colspan="7"><h2>Linked Places</h2></td></tr>
{#each aPlace.linked_places as addressLine}
- <DetailsOneRow addressLine={addressLine} bDistanceInMeters=true />
+ <DetailsOneRow addressLine={addressLine}
+ bMarkUnusedLines={true}
+ bDistanceInMeters={true} />
{/each}
{/if}
- <tr class="all-columns"><td colspan="6"><h2>Keywords</h2></td></tr>
- {#if aPlace.keywords}
- <tr class="all-columns"><td colspan="6"><h3>Name Keywords</h3></td></tr>
- {#each aPlace.keywords.name as keyword}
- <tr>
- <td>{formatKeywordToken(keyword.token)}</td>
- {#if keyword.id}
- <td>word id: {keyword.id}</td>
- {/if}
- </tr>
- {/each}
+ {#if !reverse_only}
+ <tr class="all-columns"><td colspan="7"><h2>Keywords</h2></td></tr>
+ {#if api_request_params.keywords}
- <tr class="all-columns"><td colspan="6"><h3>Address Keywords</h3></td></tr>
- {#each aPlace.keywords.address as keyword}
- <tr>
- <td>{formatKeywordToken(keyword.token)}</td>
- {#if keyword.id}
- <td>word id: {keyword.id}</td>
+ {#if place_has_keywords(aPlace)}
+ <tr class="all-columns"><td colspan="7"><h3>Name Keywords</h3></td></tr>
+ {#each aPlace.keywords.name as keyword}
+ <tr>
+ <td>{formatKeywordToken(keyword.token)}</td>
+ {#if keyword.id}
+ <td>word id: {keyword.id}</td>
+ {/if}
+ </tr>
+ {/each}
+
+ {#if aPlace.keywords.address}
+ <tr class="all-columns"><td colspan="7"><h3>Address Keywords</h3></td></tr>
+ {#each aPlace.keywords.address as keyword}
+ <tr>
+ <td>{formatKeywordToken(keyword.token)}</td>
+ {#if keyword.id}
+ <td>word id: {keyword.id}</td>
+ {/if}
+ </tr>
+ {/each}
{/if}
- </tr>
- {/each}
- {:else}
- <tr>
- <td>
- <a class="btn btn-outline-secondary btn-sm"
- href="{base_url}&keywords=1">display keywords</a>
- </td>
- </tr>
+ {:else}
+ <tr><td>Place has no keywords</td></tr>
+ {/if}
+ {:else}
+ <tr>
+ <td>
+ <a class="btn btn-outline-secondary btn-sm"
+ href="{base_url}&keywords=1">display keywords</a>
+ </td>
+ </tr>
+ {/if}
{/if}
- <tr class="all-columns"><td colspan="6"><h2>Parent Of</h2></td></tr>
- {#if aPlace.hierarchy}
-
- {#each Object.keys(aPlace.hierarchy) as type}
- <tr class="all-columns"><td colspan="6"><h3>{type}</h3></td></tr>
- {#each aPlace.hierarchy[type] as line}
- <DetailsOneRow addressLine={line} bDistanceInMeters=true />
- {/each}
- {/each}
+ <tr class="all-columns"><td colspan="7"><h2>Parent Of</h2></td></tr>
+ {#if api_request_params.hierarchy}
+ {#if aPlace.hierarchy && typeof (aPlace.hierarchy) === 'object'
+ && Object.keys(aPlace.hierarchy).length}
+ {#each Object.keys(aPlace.hierarchy) as type}
+ <tr class="all-columns"><td colspan="7"><h3>{type}</h3></td></tr>
+ {#each aPlace.hierarchy[type] as line}
+ <DetailsOneRow addressLine={line} bDistanceInMeters={true} />
+ {/each}
+ {/each}
- {#if Object.keys(aPlace.hierarchy) > 500}
- <p>There are more child objects which are not shown.</p>
+ {#if Object.keys(aPlace.hierarchy) > 500}
+ <p>There are more child objects which are not shown.</p>
+ {/if}
+ {:else}
+ <tr><td>Place is not parent of other places</td></tr>
{/if}
{:else}
<tr>
</table>
</div>
</div>
- </div>
-{:else if (location.search === '')}
- <DetailsIndex/>
-{:else}
- No such place found.
-{/if}
+ {:else if (window.location.search !== '' && api_request_finished)}
+ No such place found.
+ {/if}
+</div>
padding-left: 8px;
}
- h1 small a {
+ h1 small :global(a) {
font-size: 0.5em;
white-space: nowrap;
}
}
tr.all-columns {
- background-color: white !important;
+ background-color: white !important;
border: none;
}
tr.all-columns td {
border-top: none !important;
padding-left: 0 !important;
}
-
- .table {
- width: 100%;
- }
- .table td {
- font-size: 0.9em;
- }
- .table>thead>tr>th, .table>tbody>tr>td {
- padding: 2px 8px;
- }
- .name{
- font-weight: bold;
+ :global(span.noname){
+ color:#800;
}
+
#map-wrapper {
+ position: relative;
width:100%;
min-height: auto;
height:300px;
border: 1px solid #666;
}
-</style>
\ No newline at end of file
+</style>