]> git.openstreetmap.org Git - nominatim-ui.git/blob - src/pages/DetailsPage.svelte
Merge remote-tracking branch 'upstream/master'
[nominatim-ui.git] / src / pages / DetailsPage.svelte
1 <script>
2   import { fetch_from_api, update_html_title } from '../lib/api_utils.js';
3   import { page } from '../lib/stores.js';
4
5   import {
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 InfoRow from '../components/DetailsInfoRow.svelte';
16   import InfoRowList from '../components/DetailsInfoRowList.svelte';
17   import Map from '../components/Map.svelte';
18
19   let aPlace;
20   let base_url;
21   let api_request_params;
22   let api_request_finished = false;
23
24   function loaddata(search_params) {
25     api_request_params = {
26       place_id: search_params.get('place_id'),
27       osmtype: search_params.get('osmtype'),
28       osmid: search_params.get('osmid'),
29       class: search_params.get('class'),
30       keywords: search_params.get('keywords'),
31       addressdetails: 1,
32       hierarchy: (search_params.get('hierarchy') === '1' ? 1 : 0),
33       group_hierarchy: 1,
34       polygon_geojson: 1,
35       format: 'json'
36     };
37     api_request_finished = false;
38
39     if (api_request_params.place_id || (api_request_params.osmtype && api_request_params.osmid)) {
40
41       if (api_request_params.place_id) {
42         update_html_title('Details for ' + api_request_params.place_id);
43       } else {
44         update_html_title('Details for ' + api_request_params.osmtype + api_request_params.osmid);
45       }
46
47       fetch_from_api('details', api_request_params, function (data) {
48         window.scrollTo(0, 0);
49         api_request_finished = true;
50         aPlace = (data && !data.error) ? data : undefined;
51       });
52     } else {
53       aPlace = undefined;
54     }
55   }
56
57   function place_has_keywords(aThisPlace) {
58     // Return false if Nominatim API sends 'keywords: { name: [], address: [] }'
59     return (
60       aThisPlace.keywords && aThisPlace.keywords.name && aThisPlace.keywords.address
61       && (aThisPlace.keywords.name.length > 0 || aThisPlace.keywords.address.length > 0)
62     );
63   }
64
65   $: {
66     let pageinfo = $page;
67     if (pageinfo.tab === 'details') {
68       loaddata(pageinfo.params);
69       base_url = window.location.search;
70     }
71   }
72   $: reverse_only = Nominatim_Config.Reverse_Only;
73 </script>
74
75 <Header>
76   <SearchSectionDetails api_request_params={api_request_params}/>
77 </Header>
78
79 <div class="container">
80   {#if aPlace}
81     <div class="row">
82       <div class="col-sm-10">
83         <h1>
84           {aPlace.localname || `${formatOSMType(aPlace.osm_type)} ${aPlace.osm_id}` }
85           <small><DetailsLink feature={aPlace}>link to this page</DetailsLink></small>
86         </h1>
87       </div>
88       <div class="col-sm-2 text-end">
89         <MapIcon aPlace={aPlace} />
90       </div>
91     </div>
92     <div class="row">
93       <div class="col-md-6">
94         <table id="locationdetails" class="table table-striped table-responsive">
95           <tbody>
96             <InfoRow title="Name">
97             {#if (Array.isArray(aPlace.names)) }
98               <span class="noname fw-bold">No Name</span>
99             {:else}
100               <InfoRowList items={aPlace.names} />
101             {/if}
102             </InfoRow>
103             <InfoRow title="Type">{aPlace.category}:{aPlace.type}</InfoRow>
104             <InfoRow title="Last Updated">{aPlace.indexed_date}</InfoRow>
105             {#if (isAdminBoundary(aPlace)) }
106               <InfoRow title="Admin Level">{aPlace.admin_level}</InfoRow>
107             {/if}
108             <InfoRow title="Search Rank">{aPlace.rank_search}</InfoRow>
109             <InfoRow title="Address Rank">{aPlace.rank_address} ({formatAddressRank(aPlace.rank_address)})</InfoRow>
110             {#if aPlace.calculated_importance}
111               <InfoRow title="Importance">
112                   {aPlace.calculated_importance}
113                   {#if !aPlace.importance} (estimated){/if}
114               </InfoRow>
115             {/if}
116             <InfoRow title="Coverage">{coverageType(aPlace)}</InfoRow>
117             <InfoRow title="Centre Point (lat,lon)">
118                 {aPlace.centroid.coordinates[1]},{aPlace.centroid.coordinates[0]}
119             </InfoRow>
120             <InfoRow title="OSM">{@html osmLink(aPlace)}</InfoRow>
121             <InfoRow title="Place Id">
122                {aPlace.place_id}
123                (<a href="https://nominatim.org/release-docs/develop/api/Output/#place_id-is-not-a-persistent-id">on this server</a>)
124             </InfoRow>
125             {#if aPlace.calculated_wikipedia}
126               <InfoRow title="Wikipedia Calculated">{@html wikipediaLink(aPlace)}</InfoRow>
127             {/if}
128             <InfoRow title="Computed Postcode">
129               {#if aPlace.calculated_postcode}
130                 {aPlace.calculated_postcode}
131                 <DetailsPostcodeHint postcode={aPlace.calculated_postcode} lat={aPlace.centroid.coordinates[1]} lon={aPlace.centroid.coordinates[0]} />
132               {/if}
133             </InfoRow>
134             <InfoRow title="Address Tags"><InfoRowList items={aPlace.addresstags} /></InfoRow>
135             <InfoRow title="Extra Tags"><InfoRowList items={aPlace.extratags} /></InfoRow>
136           </tbody>
137         </table>
138       </div>
139       <div class="col-md-6">
140         <div id="map-wrapper">
141           <Map current_result={aPlace} />
142         </div>
143       </div>
144     </div>
145     <div class="row">
146       <div class="col-md-12">
147         <h2>Address</h2>
148          <table id="address" class="table table-striped table-small">
149           <thead>
150             <tr>
151               <th>Local name</th>
152               <th>Type</th>
153               <th>OSM</th>
154               <th>Address rank</th>
155               <th>Admin level</th>
156               <th>Distance</th>
157               <th></th>
158             </tr>
159           </thead>
160           <tbody>
161             {#if aPlace.address}
162               {#each aPlace.address as addressLine}
163                 <DetailsOneRow addressLine={addressLine} bMarkUnusedLines={true} bDistanceInMeters={false} />
164               {/each}
165             {/if}
166
167             {#if aPlace.linked_places}
168               <tr class="all-columns"><td colspan="7"><h2>Linked Places</h2></td></tr>
169               {#each aPlace.linked_places as addressLine}
170                 <DetailsOneRow addressLine={addressLine} bMarkUnusedLines={true} bDistanceInMeters={true} />
171               {/each}
172             {/if}
173
174             {#if !reverse_only}
175               <tr class="all-columns"><td colspan="7"><h2>Keywords</h2></td></tr>
176               {#if api_request_params.keywords}
177
178                 {#if place_has_keywords(aPlace)}
179                   <tr class="all-columns"><td colspan="7"><h3>Name Keywords</h3></td></tr>
180                   {#each aPlace.keywords.name as keyword}
181                     <tr>
182                       <td>{formatKeywordToken(keyword.token)}</td>
183                       {#if keyword.id}
184                         <td>word id: {keyword.id}</td>
185                       {/if}
186                     </tr>
187                   {/each}
188
189                   {#if aPlace.keywords.address}
190                     <tr class="all-columns"><td colspan="7"><h3>Address Keywords</h3></td></tr>
191                     {#each aPlace.keywords.address as keyword}
192                       <tr>
193                         <td>{formatKeywordToken(keyword.token)}</td>
194                         {#if keyword.id}
195                           <td>word id: {keyword.id}</td>
196                         {/if}
197                       </tr>
198                     {/each}
199                   {/if}
200                 {:else}
201                   <tr><td>Place has no keywords</td></tr>
202                 {/if}
203               {:else}
204                 <tr>
205                   <td>
206                      <a class="btn btn-outline-secondary btn-sm"
207                       href="{base_url}&keywords=1">display keywords</a>
208                   </td>
209                 </tr>
210               {/if}
211             {/if}
212
213             <tr class="all-columns"><td colspan="7"><h2>Parent Of</h2></td></tr>
214             {#if api_request_params.hierarchy}
215               {#if aPlace.hierarchy && typeof (aPlace.hierarchy) === 'object' && Object.keys(aPlace.hierarchy).length}
216                 {#each Object.keys(aPlace.hierarchy) as type}
217                   <tr class="all-columns"><td colspan="7"><h3>{type}</h3></td></tr>
218                   {#each aPlace.hierarchy[type] as line}
219                     <DetailsOneRow addressLine={line} bDistanceInMeters={true} />
220                  {/each}
221                 {/each}
222
223                 {#if Object.keys(aPlace.hierarchy) > 500}
224                   <p>There are more child objects which are not shown.</p>
225                 {/if}
226               {:else}
227                 <tr><td>Place is not parent of other places</td></tr>
228               {/if}
229             {:else}
230               <tr>
231                 <td>
232                    <a class="btn btn-outline-secondary btn-sm"
233                     href="{base_url}&hierarchy=1">display child places</a>
234                 </td>
235               </tr>
236             {/if}
237           </tbody>
238         </table>
239       </div>
240     </div>
241   {:else if (window.location.search !== '' && api_request_finished)}
242     No such place found.
243   {/if}
244 </div>
245
246
247
248 <style>
249   h1 {
250     margin: 10px 0;
251     padding-left: 8px;
252   }
253
254   h1 small :global(a) {
255     font-size: 0.5em;
256     white-space: nowrap;
257   }
258
259   h2 {
260     font-size: 2em;
261     padding-left: 8px;
262     background-color: white;
263   }
264   h3 {
265     font-size: 1.5em;
266     padding-left: 8px;
267   }
268
269   tr.all-columns {
270     background-color: white !important; 
271     border: none;
272   }
273   tr.all-columns td {
274     border-top: none !important;
275     padding-left: 0 !important;
276   }
277   :global(span.noname){
278     color:#800;
279   }
280
281   #map-wrapper {
282     position: relative;
283     width:100%;
284     min-height: auto;
285     height:300px;
286     border: 1px solid #666;
287   }
288 </style>