]> git.openstreetmap.org Git - nominatim-ui.git/commitdiff
put search,reverse,by-id links into top navbar (#75)
authormtmail <mtmail@gmx.net>
Tue, 16 Feb 2021 21:17:11 +0000 (22:17 +0100)
committerGitHub <noreply@github.com>
Tue, 16 Feb 2021 21:17:11 +0000 (22:17 +0100)
16 files changed:
src/App.svelte
src/components/DetailsIndex.svelte [deleted file]
src/components/Header.svelte
src/components/LastUpdated.svelte
src/components/PageLink.svelte
src/components/ReverseBar.svelte [deleted file]
src/components/ReverseLink.svelte
src/components/SearchBar.svelte [deleted file]
src/components/SearchSection.svelte [new file with mode: 0644]
src/components/SearchSectionDetails.svelte [new file with mode: 0644]
src/components/SearchSectionReverse.svelte [new file with mode: 0644]
src/pages/DeletablePage.svelte
src/pages/DetailsPage.svelte
src/pages/PolygonsPage.svelte
src/pages/ReversePage.svelte
src/pages/SearchPage.svelte

index ec1ca0bb2bc4b47d4912d29c6215dea80e14065c..d12017345e22290425710f49af164ce3c5db16c5 100644 (file)
@@ -4,7 +4,6 @@
 
   import { page, refresh_page } from './lib/stores.js';
 
-  import Header from './components/Header.svelte';
   import Footer from './components/Footer.svelte';
   import ReportIssueModal from './components/ReportIssueModal.svelte';
   import SearchPage from './pages/SearchPage.svelte';
@@ -21,7 +20,6 @@
 <!-- deal with back-button and other user action -->
 <svelte:window on:popstate={() => refresh_page()} />
 
-<Header/>
 {#if view === 'search'}
 <SearchPage />
 {:else if view === 'reverse'}
diff --git a/src/components/DetailsIndex.svelte b/src/components/DetailsIndex.svelte
deleted file mode 100644 (file)
index 2183597..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-<script>
-  function handleFormSubmit(event) {
-
-    let form_el = event.target;
-    let val = form_el.querySelector('input[type=edit]').value;
-    let matches = val.match(/^\s*([NWR])(\d+)\s*$/i);
-
-    if (!matches) {
-      matches = val.match(/\/(relation|way|node)\/(\d+)\s*$/);
-    }
-
-    if (!matches) {
-      alert('invalid input');
-      return;
-    }
-
-    let osmtype_short = matches[1].charAt(0).toUpperCase();
-    form_el.querySelector('input[name=osmtype]').setAttribute('value', osmtype_short);
-    form_el.querySelector('input[name=osmid]').setAttribute('value', matches[2]);
-    form_el.submit();
-  }
-</script>
-<div class="container" id="details-index-page">
-  <div class="row">
-    <div class="col-md-12">
-
-      <h1>Show details for place</h1>
-
-      <div class="search-form">
-        <h4>Search by place id</h4>
-
-        <form class="form-inline" action="details.html">
-          <input type="edit"
-                 class="form-control form-control-sm"
-                 pattern="^[0-9]+$"
-                 name="place_id"
-                 placeholder="12345" />
-          <input type="submit"
-                 class="btn btn-primary btn-sm"
-                 value="Show" />
-        </form>
-      </div>
-
-      <div class="search-form">
-        <h4>Search by OSM type and OSM id</h4>
-
-        <form on:submit|preventDefault={handleFormSubmit}
-              id="form-by-type-and-id"
-              class="form-inline"
-              action="details.html">
-          <input type="edit"
-                 class="form-control form-control-sm"
-                 pattern="^[NWR][0-9]+$"
-                 placeholder="N123 or W123 or R123" />
-          <input type="hidden" name="osmtype" />
-          <input type="hidden" name="osmid" />
-          <input type="submit" class="btn btn-primary btn-sm" value="Show" />
-        </form>
-      </div>
-
-      <div class="search-form">
-        <h4>Search by openstreetmap.org URL</h4>
-
-        <form on:submit|preventDefault={handleFormSubmit}
-              id="form-by-osm-url"
-              class="form-inline"
-              action="details.html">
-          <input type="edit"
-                 class="form-control form-control-sm"
-                 pattern=".*openstreetmap.*"
-                 placeholder="https://www.openstreetmap.org/relation/123" />
-          <input type="hidden" name="osmtype" />
-          <input type="hidden" name="osmid" />
-          <input type="submit" class="btn btn-primary btn-sm" value="Show" />
-        </form>
-      </div>
-
-    </div>
-  </div>
-</div>
-
-<style>
-  .search-form {
-    padding: 20px 10px;
-    margin: 2em 0;
-  }
-  .search-form h4 {
-    margin-top: 0;
-  }
-  .search-form .form-control{
-    margin-right: 5px;
-    width: 30em;
-  }
-</style>
index 5000cd67cd71e6724a7033b84528835dccabcd44..d38c87ca869deeb9a6faf84c58e7d06d2fa287fc 100644 (file)
@@ -1,68 +1,85 @@
 <script>
-  import LastUpdated from './LastUpdated.svelte';
   import PageLink from './PageLink.svelte';
-</script>
+  import ReverseLink from './ReverseLink.svelte';
+  import LastUpdated from './LastUpdated.svelte';
 
-<style>
-  header {
-    width: 100%;
-    padding: 5px 15px;
-    z-index: 5;
-  }
+  import { page } from '../lib/stores.js';
 
-  .brand {
-    white-space: nowrap;
-  }
+  $: view = $page.tab;
+</script>
 
-  .brand :global(a:hover) {
+<style>
+  .navbar-brand :global(a:hover) {
     text-decoration: none;
   }
 
-  .brand h1 {
+  .navbar-brand h1 {
     display: inline;
-    font-size: 1.5em;
+    font-size: 1.2em;
     color: #333;
   }
 
-  .brand img {
+  .navbar-brand img {
     display: inline-block;
     margin-right: 5px;
     margin-top: -5px;
   }
 
-  .dropdown-menu {
+  .dropdown-menu { /* need to be above map markers */
     z-index: 1005;
   }
+
+  .search-section {
+    padding: 1em 30px;
+    background-color: #f5f5f5;
+    border-top: 2px solid #ddd;
+    border-bottom: 2px solid #ddd;
+  }
 </style>
 
 <header class="container-fluid">
-  <div class="row">
-    <div class="col-4">
-      <div class="brand">
-        <PageLink page="search">
-          <img alt="logo" src="images/osm_logo.120px.png" width="30" height="30"/>
-          <h1>Nominatim</h1>
-        </PageLink>
-      </div>
-    </div>
-    <div class="col-4">
-      <LastUpdated/>
+  <nav class="navbar navbar-expand-lg navbar-light">
+    <div class="navbar-brand">
+      <PageLink page="search">
+        <img alt="logo" src="images/osm_logo.120px.png" width="30" height="30"/>
+        <h1>Nominatim</h1>
+      </PageLink>
     </div>
-    <div class="col-4 text-right">
-      <div class="dropdown">
-        <button class="dropdown-toggle btn btn-sm btn-outline-secondary" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
-          About &amp; Help
-        </button>
-        <div class="dropdown-menu dropdown-menu-right">
-          <a class="dropdown-item" href="https://nominatim.org/release-docs/develop/api/Overview/" target="_blank">API Reference</a>
-          <a class="dropdown-item" href="https://nominatim.org/release-docs/develop/api/Faq/" target="_blank">FAQ</a>
-          <a class="dropdown-item" href="https://help.openstreetmap.org/tags/nominatim/">OpenStreetMap Help</a>
-          <a class="dropdown-item" href="https://github.com/osm-search/Nominatim">Nominatim on Github</a>
-          <a class="dropdown-item" href="https://github.com/osm-search/nominatim-ui">This frontend on Github</a>
-          <div class="dropdown-divider"></div>
-          <a class="dropdown-item" href="#report-issue" data-toggle="modal" data-target="#report-modal">Report problem with results</a>
-        </div>
-      </div>
+    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
+      <span class="navbar-toggler-icon"></span>
+    </button>
+    <div class="collapse navbar-collapse" id="navbarSupportedContent">
+      <ul class="navbar-nav mr-auto">
+        <li class="nav-item {view === 'search' ? 'active' : ''}">
+          <PageLink page="search" extra_classes="nav-link ">Search</PageLink>
+        </li>
+        <li class="nav-item {view === 'reverse' ? 'active' : ''}">
+          <ReverseLink extra_classes="nav-link ">Reverse</ReverseLink>
+        </li>
+        <li class="nav-item {view === 'details' ? 'active' : ''}">
+          <PageLink page="details" extra_classes="nav-link ">Search By ID</PageLink>
+        </li>
+      </ul>
+      <ul class="navbar-nav">
+        <li class="nav-item dropdown">
+          <a class="nav-link dropdown-toggle" href="#open-about-dropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+            About &amp; Help
+          </a>
+          <div class="dropdown-menu dropdown-menu-right">
+            <a class="dropdown-item" href="https://nominatim.org/release-docs/develop/api/Overview/" target="_blank">API Reference</a>
+            <a class="dropdown-item" href="https://nominatim.org/release-docs/develop/api/Faq/" target="_blank">FAQ</a>
+            <a class="dropdown-item" href="https://help.openstreetmap.org/tags/nominatim/">OpenStreetMap Help</a>
+            <a class="dropdown-item" href="https://github.com/osm-search/Nominatim">Nominatim on Github</a>
+            <a class="dropdown-item" href="https://github.com/osm-search/nominatim-ui">This frontend on Github</a>
+            <div class="dropdown-divider"></div>
+            <a class="dropdown-item" href="#report-issue" data-toggle="modal" data-target="#report-modal">Report problem with results</a>
+          </div>
+        </li>
+      </ul>
     </div>
-  </div>
+  </nav>
 </header>
+<section class="search-section">
+  <slot/>
+</section>
+<LastUpdated/>
index f2699031779216696dd492b7536d0d746fcf1374..ccf1338895f953f6a9896c6e2676cb956f1448fc 100644 (file)
@@ -15,9 +15,8 @@
 
 <style>
   #last-updated {
-    font-size: 0.7em;
-    white-space: nowrap;
-    text-align: center;
+    font-size: 0.8rem;
+    font-style: italic;
   }
   #loading {
     display: none;
     width: 100%;
     background-color: #eee;
     z-index: 100;
-    padding: 10px;
-    text-align: center;
   }
 </style>
 
-<div id="last-updated">
-  <div id="loading">loading...</div>
-  {#if last_updated_date}
-    {#if last_api_request_url}
-      <div id="api-request">
-        Data from <a href="{last_api_request_url}">API request</a>
-        <span id="api-request-debug">
-          (<a href="{last_api_request_url}&debug=1">debug output</a>)
-        </span>
-      </div>
-    {/if}
-    Data last updated: <span id="data-date">{last_updated_date}</span>
-  {/if}
+<div id="last-updated" class="container-fluid py-2 px-4 mb-3">
+  <div class="row">
+    <div class="col-sm-6">
+      <div id="loading">loading...</div>
+      {#if last_api_request_url}
+        <div id="api-request">
+          Data from <a href="{last_api_request_url}">API request</a>
+          <span id="api-request-debug">
+            (<a href="{last_api_request_url}&debug=1">debug output</a>)
+          </span>
+        </div>
+      {/if}
+    </div>
+    <div class="col-sm-6 text-right">
+      {#if last_updated_date}
+        Data last updated: <span id="data-date">{last_updated_date}</span>
+      {/if}
+    </div>
+  </div>
 </div>
index 9205c2f726470c8e97a9adf3cccb218a7baa8fed..bccddf2bb9056c988888274efcb3628dc0e6a837 100644 (file)
@@ -2,10 +2,11 @@
 import { refresh_page } from '../lib/stores.js';
 
 export let page;
+export let extra_classes = '';
 
 function handleClick() {
   refresh_page(page);
 }
 </script>
 
-<a on:click|preventDefault|stopPropagation={handleClick} href="{page}.html"><slot></slot></a>
+<a on:click|preventDefault|stopPropagation={handleClick} href="{page}.html" class={extra_classes}><slot></slot></a>
diff --git a/src/components/ReverseBar.svelte b/src/components/ReverseBar.svelte
deleted file mode 100644 (file)
index 62227c8..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<script>
-  import UrlSubmitForm from '../components/UrlSubmitForm.svelte';
-  import DetailsLink from '../components/DetailsLink.svelte';
-  import PageLink from '../components/PageLink.svelte';
-
-  import { zoomLevels } from '../lib/helpers.js';
-  import { map_store, refresh_page } from '../lib/stores.js';
-
-  export let lat = '';
-  export let lon = '';
-  export let zoom = '';
-
-  function gotoCoordinates(newlat, newlon, newzoom) {
-    let params = new URLSearchParams();
-    params.set('lat', newlat);
-    params.set('lon', newlon);
-    params.set('zoom', newzoom || zoom);
-    refresh_page('reverse', params);
-  }
-
-  map_store.subscribe(map => {
-    if (map) {
-      map.on('click', (e) => {
-        let coords = e.latlng.wrap();
-        gotoCoordinates(coords.lat.toFixed(5), coords.lng.toFixed(5));
-      });
-    }
-  });
-
-  // common mistake is to copy&paste latitude and longitude into the 'lat' search box
-  function maybeSplitLatitude(e) {
-    var coords_split = e.target.value.split(',');
-    if (coords_split.length === 2) {
-      document.querySelector('input[name=lat]').value = L.Util.trim(coords_split[0]);
-      document.querySelector('input[name=lon]').value = L.Util.trim(coords_split[1]);
-    }
-  }
-
-</script>
-
-<div class="top-bar">
-  <UrlSubmitForm page="reverse">
-    <div class="form-group">
-      <input name="format" type="hidden" value="html">
-      <label for="reverse-lat">lat</label>
-      <input id="reverse-lat"
-             name="lat"
-             type="text"
-             class="form-control form-control-sm"
-             placeholder="latitude"
-             bind:value={lat}
-             on:change={maybeSplitLatitude} />
-      <a id="switch-coords"
-         on:click|preventDefault|stopPropagation={() => gotoCoordinates(lon, lat)}
-         class="btn btn-outline-secondary btn-sm"
-         title="switch lat and lon">&lt;&gt;</a>
-      <label for="reverse-lon">lon</label>
-      <input id="reverse-lon"
-             name="lon"
-             type="text"
-             class="form-control form-control-sm"
-             placeholder="longitude"
-             bind:value={lon} />
-      <label for="reverse-zoom">max zoom</label>
-      <select id="reverse-zoom" name="zoom" class="form-control form-control-sm" bind:value={zoom}>
-        <option value="">---</option>
-        {#each zoomLevels() as zoomTitle, i}
-          <option value="{i}">{i} - {zoomTitle}</option>
-        {/each}
-      </select>
-      <button type="submit" class="btn btn-primary btn-sm mx-1">
-        Search
-      </button>
-    </div>
-    <div class="search-type-link">
-      <DetailsLink extra_classes="mr-2">search by id</DetailsLink>
-      <PageLink page="search">forward search</PageLink>
-    </div>
-  </UrlSubmitForm>
-</div>
-
-<style>
-  .top-bar {
-    width: 100%;
-    padding: 1em 15px;
-  }
-
-  label {
-    font-weight: normal;
-    margin-left: 0.4rem;
-    margin-right: 0.4rem;
-  }
-
-  .search-type-link {
-    display: inline;
-    margin-right: 2em;
-    position: absolute;
-    right: 0
-  }
-
-  #switch-coords {
-    font-size: 0.6rem;
-    font-weight: bold;
-    cursor: pointer;
-    padding: 2px;
-    margin: 5px;
-  }
-</style>
index f6657deb5257cd6ae9b230ddb64d2c62a83361a5..2fd2c4ceebe15435286ab6b47087ee9c6ae7e9c0 100644 (file)
@@ -4,6 +4,7 @@ import { refresh_page } from '../lib/stores.js';
 export let lat;
 export let lon;
 export let zoom = null;
+export let extra_classes = '';
 
 let params = new URLSearchParams();
 let href = 'reverse.html';
@@ -29,4 +30,4 @@ $: {
 }
 </script>
 
-<a on:click|preventDefault|stopPropagation={() => refresh_page('reverse', params)} href={href}><slot></slot></a>
+<a on:click|preventDefault|stopPropagation={() => refresh_page('reverse', params)} href={href} class={extra_classes}><slot></slot></a>
diff --git a/src/components/SearchBar.svelte b/src/components/SearchBar.svelte
deleted file mode 100644 (file)
index d0890a9..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-<script>
-  import UrlSubmitForm from '../components/UrlSubmitForm.svelte';
-  import DetailsLink from '../components/DetailsLink.svelte';
-  import ReverseLink from '../components/ReverseLink.svelte';
-
-  import { map_store } from '../lib/stores.js';
-  import { get } from 'svelte/store';
-
-  export let bStructuredSearch = false;
-  export let api_request_params = {};
-  let sViewBox;
-  let lat;
-  let lon;
-
-  function map_viewbox_as_string(map) {
-    var bounds = map.getBounds();
-    var west = bounds.getWest();
-    var east = bounds.getEast();
-
-    if ((east - west) >= 360) { // covers more than whole planet
-      west = map.getCenter().lng - 179.999;
-      east = map.getCenter().lng + 179.999;
-    }
-    east = L.latLng(77, east).wrap().lng;
-    west = L.latLng(77, west).wrap().lng;
-
-    return [
-      west.toFixed(5), // left
-      bounds.getNorth().toFixed(5), // top
-      east.toFixed(5), // right
-      bounds.getSouth().toFixed(5) // bottom
-    ].join(',');
-  }
-
-  function set_viewbox(map) {
-    let use_viewbox = document.getElementById('use_viewbox');
-    if (use_viewbox && use_viewbox.checked) {
-      sViewBox = map_viewbox_as_string(map);
-    } else {
-      sViewBox = '';
-    }
-  }
-
-  function update_reverse_link(map) {
-    let center_lat_lng = map.wrapLatLng(map.getCenter());
-    lat = center_lat_lng.lat.toFixed(5);
-    lon = center_lat_lng.lng.toFixed(5);
-  }
-
-  map_store.subscribe(map => {
-    if (!map) { return; }
-
-    map.on('move', function () {
-      set_viewbox(map);
-      update_reverse_link(map);
-    });
-
-    map.on('load', function () {
-      set_viewbox(map);
-      update_reverse_link(map);
-    });
-  });
-
-  function reset_viewbox() {
-    let map = get(map_store);
-    if (map) { set_viewbox(map); }
-  }
-
-  function set_bounded(e) {
-    console.log('setting', e.target);
-    document.querySelector('input[name=bounded]').value = e.target.checked ? 1 : '';
-  }
-
-  function set_dedupe(e) {
-    document.querySelector('input[name=dedupe]').value = e.target.checked ? 1 : '';
-  }
-
-  function set_api_param(e) {
-    document.querySelector('input[name=' + e.target.dataset.apiParam + ']').value = e.target.value;
-  }
-</script>
-
-<div class="top-bar">
-  <ul class="nav nav-tabs">
-    <li class="nav-item">
-      <a class="nav-link" class:active={!bStructuredSearch} data-toggle="tab" href="#simple">simple</a>
-    </li>
-    <li class="nav-item">
-      <a class="nav-link" class:active={bStructuredSearch} data-toggle="tab" href="#structured">structured</a>
-    </li>
-    <div class="search-type-link">
-      <DetailsLink extra_classes="mr-2">search by id</DetailsLink>
-      <ReverseLink lat={lat} lon={lon}>reverse search</ReverseLink>
-    </div>
-  </ul>
-  <div class="tab-content p-2">
-    <div class="tab-pane" class:active={!bStructuredSearch} id="simple" role="tabpanel">
-      <UrlSubmitForm page="search">
-        <input id="q"
-               name="q"
-               type="text"
-               class="form-control form-control-sm"
-               placeholder="Search"
-               value="{api_request_params.q || ''}" />
-
-        <div class="form-group search-button-group">
-          <button type="submit" class="btn btn-primary btn-sm mx-1">Search</button>
-          <input type="hidden" name="viewbox" value="{sViewBox || ''}" />
-          <input type="hidden" name="dedupe" value="{!api_request_params.dedupe ? '' : 1}" />
-          <input type="hidden" name="bounded" value="{api_request_params.bounded ? 1 : ''}" />
-          <input type="hidden" name="accept-language" value="{api_request_params['accept-language'] || ''}" />
-          <input type="hidden" name="countrycodes" value="{api_request_params.countrycodes || ''}" />
-          <input type="hidden" name="limit" value="{api_request_params.limit || ''}" />
-          <input type="hidden" name="polygon_threshold" value="{api_request_params.polygon_threshold || ''}" />
-        </div>
-      </UrlSubmitForm>
-    </div>
-    <div class="tab-pane" class:active={bStructuredSearch} id="structured" role="tabpanel">
-      <UrlSubmitForm page="search">
-        <input name="street" type="text" class="form-control form-control-sm mr-1"
-               placeholder="House number/Street"
-               value="{api_request_params.street || ''}" />
-        <input name="city" type="text" class="form-control form-control-sm mr-1"
-               placeholder="City"
-               value="{api_request_params.city || ''}" />
-        <input id="county" name="county" type="text" class="form-control form-control-sm mr-1"
-               placeholder="County"
-               value="{api_request_params.county || ''}" />
-        <input name="state" type="text" class="form-control form-control-sm mr-1"
-               placeholder="State"
-               value="{api_request_params.state || ''}" />
-        <input name="country" type="text" class="form-control form-control-sm mr-1"
-               placeholder="Country"
-               value="{api_request_params.country || ''}" />
-        <input name="postalcode" type="text" class="form-control form-control-sm mr-1"
-               placeholder="Postal Code"
-               value="{api_request_params.postalcode || ''}" />
-
-        <div class="form-group search-button-group">
-          <button type="submit" class="btn btn-primary btn-sm mx-1">Search</button>
-          <input type="hidden" name="viewbox" value="{sViewBox || ''}" />
-          <input type="hidden" name="dedupe" value="{!api_request_params.dedupe ? '' : 1}" />
-          <input type="hidden" name="bounded" value="{api_request_params.bounded ? 1 : ''}" />
-          <input type="hidden" name="accept-language" value="{api_request_params['accept-language'] || ''}" />
-          <input type="hidden" name="countrycodes" value="{api_request_params.countrycodes || ''}" />
-          <input type="hidden" name="limit" value="{api_request_params.limit || ''}" />
-          <input type="hidden" name="polygon_threshold" value="{api_request_params.polygon_threshold || ''}" />
-        </div>
-      </UrlSubmitForm>
-    </div>
-    <!-- Additional options -->
-    <a href="#advanced" class="btn btn-outline-secondary btn-sm" data-toggle="collapse" data-target="#searchAdvancedOptions" role="button" aria-expanded="false" aria-controls="collapseAdvancedOptions">
-      Advanced options
-    </a>
-    <div class="collapse" id="searchAdvancedOptions">
-      <div id="searchAdvancedOptionsContent">
-          <div class="form-check form-check-inline">
-            <span><input type="checkbox" class="form-check-input api-param-setting"
-                   id="use_viewbox" checked={api_request_params.viewbox} on:change={reset_viewbox}>
-            <label class="form-check-label" for="use_viewbox">apply viewbox</label></span>
-            <span><input type="checkbox" class="form-check-input api-param-setting"
-                   id="option_bounded" checked={!!api_request_params.bounded} on:change={set_bounded}>
-            <label class="form-check-label" for="option_bounded">bounded to viewbox</label></span>
-            <span><input type="checkbox" class="form-check-input api-param-setting"
-                   id="option_dedupe" checked={!!api_request_params.dedupe} on:change={set_dedupe}>
-            <label class="form-check-label" for="option_dedupe">deduplicate results</label></span>
-          </div>
-          <div class="form-check form-check-inline">
-            <span><label class="form-check-label" for="option_limit">Maximum number of results: </label>
-            <input type="number" class="form-check-input api-param-setting" data-api-param="limit" id="option_limit" size="5" min="1" max="50" value="{api_request_params.limit || ''}" on:change={set_api_param}></span>
-            <span><label class="form-check-label" for="option_polygon_threashold">Polygon simplification: </label>
-            <input type="number" class="form-check-input api-param-setting" data-api-param="polygon_threshold" id="option_polygon_threshold" size="5" min="0.0" step="0.01" value="{api_request_params.polygon_threshold || ''}" on:change={set_api_param}></span>
-          </div>
-          <div class="form-check form-check-inline">
-            <span><label class="form-check-label" for="accept_lang">Languages: </label>
-            <input type="text" placeholder="e.g. en,zh-Hant" class="form-check-input api-param-setting" data-api-param="accept-language" id="accept_lang" size="15" value="{api_request_params['accept-language'] || ''}" on:change={set_api_param}></span>
-            <span><label class="form-check-label" for="option_ccode">Countries: </label>
-            <input type="text" placeholder="e.g. de,gb" class="form-check-input api-param-setting" data-api-param="countrycodes" id="option_ccode" size="15" value="{api_request_params.countrycodes || ''}" on:change={set_api_param}></span>
-          </div>
-       </div>
-    </div>
-  </div> <!-- /tab-content -->
-</div> <!-- /top-bar -->
-
-<style>
-  .top-bar {
-    width: 100%;
-    padding: 1em 15px;
-  }
-
-  .top-bar #q {
-    max-width: 500px;
-  }
-
-  .tab-content {
-    border: 1px solid #ddd;
-    border-top: none;
-    display: flex;
-    align-items: baseline
-  }
-
-  #q {
-    min-width: 500px;
-  }
-  @media (max-width: 850px) {
-    #q {
-      min-width: 400px;
-    }
-  }
-
-  label {
-    font-weight: normal;
-    margin-left: 0.4rem;
-    margin-right: 0.4rem;
-  }
-
-  #searchAdvancedOptionsContent {
-    display: flex;
-    flex-direction: column;
-    padding: 0 10px
-  }
-
-  #searchAdvancedOptionsContent label {
-    padding: 0 3px;
-  }
-
-  #searchAdvancedOptionsContent span {
-    padding: 4px 10px;
-  }
-
-  .search-type-link {
-    display: inline;
-    margin-right: 2em;
-    position: absolute;
-    right: 0
-  }
-
-  @media (max-width: 768px) {
-    .search-button-group {
-      display: inline;
-    }
-  }
-</style>
diff --git a/src/components/SearchSection.svelte b/src/components/SearchSection.svelte
new file mode 100644 (file)
index 0000000..9bc5090
--- /dev/null
@@ -0,0 +1,225 @@
+<script>
+  import UrlSubmitForm from '../components/UrlSubmitForm.svelte';
+
+  import { map_store } from '../lib/stores.js';
+  import { get } from 'svelte/store';
+
+  export let bStructuredSearch = false;
+  export let api_request_params = {};
+  let sViewBox;
+  let lat;
+  let lon;
+
+  function map_viewbox_as_string(map) {
+    var bounds = map.getBounds();
+    var west = bounds.getWest();
+    var east = bounds.getEast();
+
+    if ((east - west) >= 360) { // covers more than whole planet
+      west = map.getCenter().lng - 179.999;
+      east = map.getCenter().lng + 179.999;
+    }
+    east = L.latLng(77, east).wrap().lng;
+    west = L.latLng(77, west).wrap().lng;
+
+    return [
+      west.toFixed(5), // left
+      bounds.getNorth().toFixed(5), // top
+      east.toFixed(5), // right
+      bounds.getSouth().toFixed(5) // bottom
+    ].join(',');
+  }
+
+  function set_viewbox(map) {
+    let use_viewbox = document.getElementById('use_viewbox');
+    if (use_viewbox && use_viewbox.checked) {
+      sViewBox = map_viewbox_as_string(map);
+    } else {
+      sViewBox = '';
+    }
+  }
+
+  function update_reverse_link(map) {
+    let center_lat_lng = map.wrapLatLng(map.getCenter());
+    lat = center_lat_lng.lat.toFixed(5);
+    lon = center_lat_lng.lng.toFixed(5);
+  }
+
+  map_store.subscribe(map => {
+    if (!map) { return; }
+
+    map.on('move', function () {
+      set_viewbox(map);
+      update_reverse_link(map);
+    });
+
+    map.on('load', function () {
+      set_viewbox(map);
+      update_reverse_link(map);
+    });
+  });
+
+  function reset_viewbox() {
+    let map = get(map_store);
+    if (map) { set_viewbox(map); }
+  }
+
+  function set_bounded(e) {
+    document.querySelector('input[name=bounded]').value = e.target.checked ? 1 : '';
+  }
+
+  function set_dedupe(e) {
+    document.querySelector('input[name=dedupe]').value = e.target.checked ? 1 : '';
+  }
+
+  function set_api_param(e) {
+    document.querySelector('input[name=' + e.target.dataset.apiParam + ']').value = e.target.value;
+  }
+</script>
+
+<ul class="nav nav-tabs">
+  <li class="nav-item">
+    <a class="nav-link" class:active={!bStructuredSearch} data-toggle="tab" href="#simple">Simple</a>
+  </li>
+  <li class="nav-item">
+    <a class="nav-link" class:active={bStructuredSearch} data-toggle="tab" href="#structured">Structured</a>
+  </li>
+</ul>
+
+<div class="tab-content p-2">
+  <div class="tab-pane" class:active={!bStructuredSearch} id="simple" role="tabpanel">
+    <UrlSubmitForm page="search">
+      <input id="q"
+             name="q"
+             type="text"
+             class="form-control form-control-sm"
+             placeholder="Search"
+             value="{api_request_params.q || ''}" />
+
+      <div class="form-group search-button-group">
+        <button type="submit" class="btn btn-primary btn-sm mx-1">Search</button>
+        <input type="hidden" name="viewbox" value="{sViewBox || ''}" />
+        <input type="hidden" name="dedupe" value="{!api_request_params.dedupe ? '' : 1}" />
+        <input type="hidden" name="bounded" value="{api_request_params.bounded ? 1 : ''}" />
+        <input type="hidden" name="accept-language" value="{api_request_params['accept-language'] || ''}" />
+        <input type="hidden" name="countrycodes" value="{api_request_params.countrycodes || ''}" />
+        <input type="hidden" name="limit" value="{api_request_params.limit || ''}" />
+        <input type="hidden" name="polygon_threshold" value="{api_request_params.polygon_threshold || ''}" />
+      </div>
+    </UrlSubmitForm>
+  </div>
+  <div class="tab-pane" class:active={bStructuredSearch} id="structured" role="tabpanel">
+    <UrlSubmitForm page="search">
+      <input name="street" type="text" class="form-control form-control-sm mr-1"
+             placeholder="House number/Street"
+             value="{api_request_params.street || ''}" />
+      <input name="city" type="text" class="form-control form-control-sm mr-1"
+             placeholder="City"
+             value="{api_request_params.city || ''}" />
+      <input id="county" name="county" type="text" class="form-control form-control-sm mr-1"
+             placeholder="County"
+             value="{api_request_params.county || ''}" />
+      <input name="state" type="text" class="form-control form-control-sm mr-1"
+             placeholder="State"
+             value="{api_request_params.state || ''}" />
+      <input name="country" type="text" class="form-control form-control-sm mr-1"
+             placeholder="Country"
+             value="{api_request_params.country || ''}" />
+      <input name="postalcode" type="text" class="form-control form-control-sm mr-1"
+             placeholder="Postal Code"
+             value="{api_request_params.postalcode || ''}" />
+
+      <div class="form-group search-button-group">
+        <button type="submit" class="btn btn-primary btn-sm mx-1">Search</button>
+        <input type="hidden" name="viewbox" value="{sViewBox || ''}" />
+        <input type="hidden" name="dedupe" value="{!api_request_params.dedupe ? '' : 1}" />
+        <input type="hidden" name="bounded" value="{api_request_params.bounded ? 1 : ''}" />
+        <input type="hidden" name="accept-language" value="{api_request_params['accept-language'] || ''}" />
+        <input type="hidden" name="countrycodes" value="{api_request_params.countrycodes || ''}" />
+        <input type="hidden" name="limit" value="{api_request_params.limit || ''}" />
+        <input type="hidden" name="polygon_threshold" value="{api_request_params.polygon_threshold || ''}" />
+      </div>
+    </UrlSubmitForm>
+  </div>
+  <!-- Additional options -->
+  <a href="#advanced" class="btn btn-outline-secondary btn-sm" data-toggle="collapse" data-target="#searchAdvancedOptions" role="button" aria-expanded="false" aria-controls="collapseAdvancedOptions">
+    Advanced options
+  </a>
+  <div class="collapse" id="searchAdvancedOptions">
+    <div id="searchAdvancedOptionsContent">
+        <div class="form-check form-check-inline">
+          <span><input type="checkbox" class="form-check-input api-param-setting"
+                 id="use_viewbox" checked={api_request_params.viewbox} on:change={reset_viewbox}>
+          <label class="form-check-label" for="use_viewbox">apply viewbox</label></span>
+          <span><input type="checkbox" class="form-check-input api-param-setting"
+                 id="option_bounded" checked={!!api_request_params.bounded} on:change={set_bounded}>
+          <label class="form-check-label" for="option_bounded">bounded to viewbox</label></span>
+          <span><input type="checkbox" class="form-check-input api-param-setting"
+                 id="option_dedupe" checked={!!api_request_params.dedupe} on:change={set_dedupe}>
+          <label class="form-check-label" for="option_dedupe">deduplicate results</label></span>
+        </div>
+        <div class="form-check form-check-inline">
+          <span><label class="form-check-label" for="option_limit">Maximum number of results: </label>
+          <input type="number" class="form-check-input api-param-setting" data-api-param="limit" id="option_limit" size="5" min="1" max="50" value="{api_request_params.limit || ''}" on:change={set_api_param}></span>
+          <span><label class="form-check-label" for="option_polygon_threashold">Polygon simplification: </label>
+          <input type="number" class="form-check-input api-param-setting" data-api-param="polygon_threshold" id="option_polygon_threshold" size="5" min="0.0" step="0.01" value="{api_request_params.polygon_threshold || ''}" on:change={set_api_param}></span>
+        </div>
+        <div class="form-check form-check-inline">
+          <span><label class="form-check-label" for="accept_lang">Languages: </label>
+          <input type="text" placeholder="e.g. en,zh-Hant" class="form-check-input api-param-setting" data-api-param="accept-language" id="accept_lang" size="15" value="{api_request_params['accept-language'] || ''}" on:change={set_api_param}></span>
+          <span><label class="form-check-label" for="option_ccode">Countries: </label>
+          <input type="text" placeholder="e.g. de,gb" class="form-check-input api-param-setting" data-api-param="countrycodes" id="option_ccode" size="15" value="{api_request_params.countrycodes || ''}" on:change={set_api_param}></span>
+        </div>
+     </div>
+  </div>
+</div> <!-- /tab-content -->
+
+<style>
+  .nav-tabs {
+    font-size: 0.8em;
+  }
+
+  #q {
+    max-width: 500px;
+  }
+
+  .tab-content {
+    display: flex;
+    align-items: baseline
+  }
+
+  #q {
+    min-width: 500px;
+  }
+  @media (max-width: 850px) {
+    #q {
+      min-width: 400px;
+    }
+  }
+
+  label {
+    font-weight: normal;
+    margin-left: 0.4rem;
+    margin-right: 0.4rem;
+  }
+
+  #searchAdvancedOptionsContent {
+    display: flex;
+    flex-direction: column;
+    padding: 0 10px
+  }
+
+  #searchAdvancedOptionsContent label {
+    padding: 0 3px;
+  }
+
+  #searchAdvancedOptionsContent span {
+    padding: 4px 10px;
+  }
+
+  @media (max-width: 768px) {
+    .search-button-group {
+      display: inline;
+    }
+  }
+</style>
diff --git a/src/components/SearchSectionDetails.svelte b/src/components/SearchSectionDetails.svelte
new file mode 100644 (file)
index 0000000..7429dbd
--- /dev/null
@@ -0,0 +1,96 @@
+<script>
+  export let api_request_params = {};
+
+  function handleFormSubmit(event) {
+
+    let form_el = event.target;
+    let val = form_el.querySelector('input[type=edit]').value;
+    let matches = val.match(/^\s*([NWR])(\d+)\s*$/i);
+
+    if (!matches) {
+      matches = val.match(/\/(relation|way|node)\/(\d+)\s*$/);
+    }
+
+    if (!matches) {
+      alert('invalid input');
+      return;
+    }
+
+    let osmtype_short = matches[1].charAt(0).toUpperCase();
+    form_el.querySelector('input[name=osmtype]').setAttribute('value', osmtype_short);
+    form_el.querySelector('input[name=osmid]').setAttribute('value', matches[2]);
+    form_el.submit();
+  }
+</script>
+
+<ul class="nav nav-tabs">
+  <li class="nav-item">
+    <a class="nav-link" data-toggle="tab" href="#by-osm-type-and-id" class:active={!api_request_params.place_id}>OSM type and OSM id</a>
+  </li>
+  <li class="nav-item">
+    <a class="nav-link" data-toggle="tab" href="#by-place-id" class:active={api_request_params.place_id}>Place id</a>
+  </li>
+  <li class="nav-item">
+    <a class="nav-link" data-toggle="tab" href="#by-osm-url">openstreetmap.org URL</a>
+  </li>
+</ul>
+
+<div class="tab-content">
+  <div class="tab-pane" id="by-osm-type-and-id" role="tabpanel" class:active={!api_request_params.place_id}>
+    <form on:submit|preventDefault={handleFormSubmit}
+          id="form-by-type-and-id"
+          class="form-inline"
+          action="details.html">
+      <input type="edit"
+             class="form-control form-control-sm"
+             pattern="^[NWR][0-9]+$"
+             placeholder="e.g. N123 or W123 or R123"
+             value="{api_request_params.osmtype || ''}{api_request_params.osmid || ''}" />
+      <input type="hidden" name="osmtype" />
+      <input type="hidden" name="osmid" />
+      <input type="submit" class="btn btn-primary btn-sm" value="Show" />
+    </form>
+  </div>
+
+  <div class="tab-pane" id="by-place-id" role="tabpanel" class:active={api_request_params.place_id}>
+    <form class="form-inline" action="details.html">
+      <input type="edit"
+             class="form-control form-control-sm"
+             pattern="^[0-9]+$"
+             name="place_id"
+             placeholder="e.g. 12345"
+             value="{api_request_params.place_id || ''}" />
+      <input type="submit"
+             class="btn btn-primary btn-sm"
+             value="Show" />
+    </form>
+  </div>
+
+  <div class="tab-pane" id="by-osm-url" role="tabpanel">
+    <form on:submit|preventDefault={handleFormSubmit}
+          id="form-by-osm-url"
+          class="form-inline"
+          action="details.html">
+      <input type="url"
+             class="form-control form-control-sm"
+             pattern=".*openstreetmap.*"
+             placeholder="e.g. https://www.openstreetmap.org/relation/123" />
+      <input type="hidden" name="osmtype" />
+      <input type="hidden" name="osmid" />
+      <input type="submit" class="btn btn-primary btn-sm" value="Show" />
+    </form>
+  </div>
+</div>
+
+<style>
+  .nav-tabs {
+    font-size: 0.8em;
+  }
+  .tab-pane {
+    padding-top: 1rem;
+  }
+  form .form-control{
+    margin-right: 5px;
+    width: 30em;
+  }
+</style>
diff --git a/src/components/SearchSectionReverse.svelte b/src/components/SearchSectionReverse.svelte
new file mode 100644 (file)
index 0000000..108e3b6
--- /dev/null
@@ -0,0 +1,88 @@
+<script>
+  import UrlSubmitForm from '../components/UrlSubmitForm.svelte';
+
+  import { zoomLevels } from '../lib/helpers.js';
+  import { map_store, refresh_page } from '../lib/stores.js';
+
+  export let lat = '';
+  export let lon = '';
+  export let zoom = '';
+
+  function gotoCoordinates(newlat, newlon, newzoom) {
+    let params = new URLSearchParams();
+    params.set('lat', newlat);
+    params.set('lon', newlon);
+    params.set('zoom', newzoom || zoom);
+    refresh_page('reverse', params);
+  }
+
+  map_store.subscribe(map => {
+    if (map) {
+      map.on('click', (e) => {
+        let coords = e.latlng.wrap();
+        gotoCoordinates(coords.lat.toFixed(5), coords.lng.toFixed(5));
+      });
+    }
+  });
+
+  // common mistake is to copy&paste latitude and longitude into the 'lat' search box
+  function maybeSplitLatitude(e) {
+    var coords_split = e.target.value.split(',');
+    if (coords_split.length === 2) {
+      document.querySelector('input[name=lat]').value = L.Util.trim(coords_split[0]);
+      document.querySelector('input[name=lon]').value = L.Util.trim(coords_split[1]);
+    }
+  }
+
+</script>
+
+<UrlSubmitForm page="reverse">
+  <div class="form-group">
+    <input name="format" type="hidden" value="html">
+    <label for="reverse-lat">lat</label>
+    <input id="reverse-lat"
+           name="lat"
+           type="text"
+           class="form-control form-control-sm"
+           placeholder="latitude"
+           bind:value={lat}
+           on:change={maybeSplitLatitude} />
+    <a id="switch-coords"
+       on:click|preventDefault|stopPropagation={() => gotoCoordinates(lon, lat)}
+       class="btn btn-outline-secondary btn-sm"
+       title="switch lat and lon">&lt;&gt;</a>
+    <label for="reverse-lon">lon</label>
+    <input id="reverse-lon"
+           name="lon"
+           type="text"
+           class="form-control form-control-sm"
+           placeholder="longitude"
+           bind:value={lon} />
+    <label for="reverse-zoom">max zoom</label>
+    <select id="reverse-zoom" name="zoom" class="form-control form-control-sm" bind:value={zoom}>
+      <option value="">---</option>
+      {#each zoomLevels() as zoomTitle, i}
+        <option value="{i}">{i} - {zoomTitle}</option>
+      {/each}
+    </select>
+    <button type="submit" class="btn btn-primary btn-sm mx-1">
+      Search
+    </button>
+  </div>
+</UrlSubmitForm>
+
+<style>
+  label {
+    font-weight: normal;
+    margin-left: 0.4rem;
+    margin-right: 0.4rem;
+  }
+
+  #switch-coords {
+    font-size: 0.6rem;
+    font-weight: bold;
+    cursor: pointer;
+    padding: 2px;
+    margin: 5px;
+  }
+</style>
index 1d6b91eb8f6dd92f05fc5af8483214a5167cbcf9..6bc80ce98d34c32b671475c4c79ef2b6092833a6 100644 (file)
@@ -3,6 +3,7 @@
   import { fetch_from_api, update_html_title } from '../lib/api_utils.js';
   import { osmLink } from '../lib/helpers.js';
 
+  import Header from '../components/Header.svelte';
   import DetailsLink from '../components/DetailsLink.svelte';
 
   let aPolygons = [];
@@ -16,6 +17,7 @@
   onMount(loaddata);
 </script>
 
+<Header/>
 <div class="container">
   <div class="row">
     <div class="col-sm-12">
index 585ce3be5ec3b37cba744976e8da035204a9db2b..d48005038769bf9904358645002ac27f98dcb845 100644 (file)
@@ -6,8 +6,9 @@
     osmLink, wikipediaLink, coverageType, isAdminBoundary,
     formatAddressRank, formatKeywordToken
   } 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 Map from '../components/Map.svelte';
   let errorResponse;
   let base_url = window.location.search;
   let current_result;
+  let api_request_params;
 
   function loaddata(search_params) {
-    var api_request_params = {
+    api_request_params = {
       place_id: search_params.get('place_id'),
       osmtype: search_params.get('osmtype'),
       osmid: search_params.get('osmid'),
@@ -63,6 +65,9 @@
   }
 </script>
 
+<Header>
+  <SearchSectionDetails api_request_params={api_request_params}/>
+</Header>
 {#if errorResponse}
   {errorResponse.error.message}
 {/if}
     </div>
   </div>
 {:else if (window.location.search === '')}
-  <DetailsIndex/>
+  <!-- <DetailsIndex/> -->
 {:else}
   No such place found.
 {/if}
index e61d89a09035d9ce3a03276c2883df887ce47ec0..0dc8a202b8ea65541056ec138e1d06cf386640d9 100644 (file)
@@ -3,6 +3,8 @@
   import { fetch_from_api, update_html_title } from '../lib/api_utils.js';
   import { formatOSMType, osmLink } from '../lib/helpers.js';
 
+  import Header from '../components/Header.svelte';
+
   let aPolygons = [];
 
   function loaddata() {
@@ -14,7 +16,7 @@
   onMount(loaddata);
 </script>
 
-
+<Header/>
 <div class="container">
   <div class="row">
     <div class="col-sm-12">
index a990646a392ff3b61bef96869a3230466a4739a4..bdc7811b82708ceef306663e02b678c831b2fbc4 100644 (file)
@@ -3,7 +3,8 @@
   import { get_config_value } from '../lib/config_reader.js';
   import { fetch_from_api, update_html_title } from '../lib/api_utils.js';
 
-  import ReverseBar from '../components/ReverseBar.svelte';
+  import Header from '../components/Header.svelte';
+  import SearchSectionReverse from '../components/SearchSectionReverse.svelte';
   import ResultsList from '../components/ResultsList.svelte';
   import Map from '../components/Map.svelte';
 
@@ -52,7 +53,9 @@
   }
 </script>
 
-<ReverseBar {...api_request_params} />
+<Header>
+  <SearchSectionReverse {...api_request_params} />
+</Header>
 
 <div id="content">
   <div class="sidebar">
index ae9aec8b84cdd780e8ef702ebb17fdd8b14ee647..4870cc44a2e3a418843daa19ee8a66dca158f421 100644 (file)
@@ -3,7 +3,8 @@
   import { get_config_value } from '../lib/config_reader.js';
   import { fetch_from_api, update_html_title } from '../lib/api_utils.js';
 
-  import SearchBar from '../components/SearchBar.svelte';
+  import Header from '../components/Header.svelte';
+  import SearchSection from '../components/SearchSection.svelte';
   import ResultsList from '../components/ResultsList.svelte';
   import Map from '../components/Map.svelte';
 
@@ -62,7 +63,9 @@
   }
 </script>
 
-<SearchBar api_request_params={api_request_params} bStructuredSearch={bStructuredSearch} />
+<Header>
+  <SearchSection api_request_params={api_request_params} bStructuredSearch={bStructuredSearch} />
+</Header>
 
 <div id="content">
   <div class="sidebar">