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';
<!-- deal with back-button and other user action -->
<svelte:window on:popstate={() => refresh_page()} />
-<Header/>
{#if view === 'search'}
<SearchPage />
{:else if view === 'reverse'}
+++ /dev/null
-<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>
<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 & 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 & 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/>
<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>
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>
+++ /dev/null
-<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"><></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>
export let lat;
export let lon;
export let zoom = null;
+export let extra_classes = '';
let params = new URLSearchParams();
let href = 'reverse.html';
}
</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>
+++ /dev/null
-<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>
--- /dev/null
+<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>
--- /dev/null
+<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>
--- /dev/null
+<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"><></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>
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 = [];
onMount(loaddata);
</script>
+<Header/>
<div class="container">
<div class="row">
<div class="col-sm-12">
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'),
}
</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}
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() {
onMount(loaddata);
</script>
-
+<Header/>
<div class="container">
<div class="row">
<div class="col-sm-12">
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';
}
</script>
-<ReverseBar {...api_request_params} />
+<Header>
+ <SearchSectionReverse {...api_request_params} />
+</Header>
<div id="content">
<div class="sidebar">
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';
}
</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">