Skip to content

Commit

Permalink
Search: use maptiler geocoding api #55
Browse files Browse the repository at this point in the history
  • Loading branch information
CharleneOlsen committed Jul 25, 2021
1 parent 9986a98 commit f0fa54e
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 106 deletions.
2 changes: 1 addition & 1 deletion public/oauth-token.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ <h1>OsmAPP auth popup</h1>
</div>

<script>
document.getElementById("noredirect").style.display = "none"
document.getElementById('noredirect').style.display = 'none';
if (opener && opener.authComplete) {
opener.authComplete(window.location.href);
window.close();
Expand Down
2 changes: 1 addition & 1 deletion src/components/FeaturePanel/EditDialog/createNoteText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const createNoteText = (

const noteText = [];
if (!feature.point) {
const {subclass} = feature.properties;
const { subclass } = feature.properties;
noteText.push(
hasName(feature) ? `${getLabel(feature)} (${subclass}):` : subclass,
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/SearchBox/AutocompleteInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const AutocompleteInput = ({
inputValue={inputValue}
options={options}
filterOptions={(x) => x}
getOptionLabel={(option) => option.display_name}
getOptionLabel={(option) => option.place_name}
onChange={onSelectedFactory(setFeature, setPreview, setView, mobileMode)}
onHighlightChange={onHighlightFactory(setPreview)}
autoComplete
Expand Down
18 changes: 12 additions & 6 deletions src/components/SearchBox/SearchBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { intl, t } from '../../services/intl';
import { ClosePanelButton } from '../utils/ClosePanelButton';
import { isDesktop } from '../helpers';

const apiKey = '7dlhLl3hiXQ1gsth0kGu'; // todo merge with consts.ts

const TopPanel = styled.div`
position: absolute;
height: 72px;
Expand Down Expand Up @@ -48,27 +50,31 @@ const SearchIconButton = styled(IconButton)`
`;

// TODO maybe search also in displayed vector map (queryFeatures)
const getApiUrl = (bbox, inputValue) =>
`https://nominatim.openstreetmap.org/search?format=json&addressdetails=1&accept-language=${intl.lang}&viewbox=${bbox}&q=${inputValue}`; // polygon_geojson=1&polygon_threshold=0.1
const getApiUrl = (center, inputValue) =>
`https://api.maptiler.com/geocoding/${inputValue}.json?key=${apiKey}&language=${intl.lang}&promixity=${center}`;

const fetchNominatim = throttle(async (inputValue, bbox, setOptions) => {
const options = await fetchJson(getApiUrl(bbox, inputValue));
const fetchOptions = throttle(async (inputValue, center, setOptions) => {
const maptilerResponse = await fetchJson(getApiUrl(center, inputValue));
const options = maptilerResponse.features;
setOptions(options || []);
}, 400);

const SearchBox = () => {
const { featureShown, setFeature } = useFeatureContext();
const { bbox } = useMapStateContext();
const { view } = useMapStateContext();
const [inputValue, setInputValue] = useState('');
const [options, setOptions] = useState([]);
const autocompleteRef = useRef();

const [, lat, lon] = view;
const center = [lon, lat];

React.useEffect(() => {
if (inputValue === '') {
setOptions([]);
return;
}
fetchNominatim(inputValue, bbox, setOptions);
fetchOptions(inputValue, center, setOptions);
}, [inputValue]);

const closePanel = () => {
Expand Down
17 changes: 16 additions & 1 deletion src/components/SearchBox/onSelectedFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,22 @@ import { getShortId, getUrlOsmId } from '../../services/helpers';
import { addFeatureCenterToCache } from '../../services/osmApi';

const getSkeleton = (location) => {
const { lat, lon, osm_type: type, osm_id: id, display_name: name } = location;
const { lat, lon, properties, place_name: name } = location;
const { osm_id: osmId } = properties;
let type; let id;
if (osmId.startsWith('relation')) {
type = 'relation';
id = osmId.substr(8);
}
if (osmId.startsWith('way')) {
type = 'way';
id = osmId.substr(3);
}
if (osmId.startsWith('node')) {
type = 'node';
id = osmId.substr(4);
}

return {
loading: true,
skeleton: true,
Expand Down
151 changes: 62 additions & 89 deletions src/components/SearchBox/renderOptionFactory.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,41 @@
import match from 'autosuggest-highlight/match';
import parse from 'autosuggest-highlight/parse';
import Grid from '@material-ui/core/Grid';
import Typography from '@material-ui/core/Typography';
import React from 'react';
import styled from 'styled-components';
import { useMapStateContext } from '../utils/MapStateContext';
import { getPoiClass } from '../../services/getPoiClass';
import Maki from '../utils/Maki';

// [
// {
// place_id: 93538005,
// licence:
// 'Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright',
// osm_type: 'way',
// osm_id: 31134664,
// boundingbox: ['50.0902337', '50.0923471', '14.3210646', '14.3216013'],
// lat: '50.0913465',
// lon: '14.3213162',
// display_name:
// 'Ke Džbánu, Liboc, Praha, okres Hlavní město Praha, Hlavní město Praha, Praha, 16100, Česká republika',
// class: 'highway',
// type: 'residential',
// importance: 0.19999999999999998,
// address: {
// road: 'Ke Džbánu',
// suburb: 'Liboc',
// city: 'Praha',
// county: 'okres Hlavní město Praha',
// state: 'Praha',
// postcode: '16100',
// country: 'Česká republika',
// country_code: 'cz',
// },
// geojson: {
// type: 'LineString',
// coordinates: [
// [14.3216013, 50.0902337],
// [14.3210646, 50.0923471],
// ],
// },
// },
// ];
/** maptiler
{
id: 'city.2273338',
type: 'Feature',
place_type: ['city'],
relevance: 1,
properties: {
osm_id: 'relation2273338',
},
text: 'Praha',
place_name: 'Praha, Region of Banská Bystrica',
bbox: [19.47208254, 48.34734753, 19.52180047, 48.39446473],
center: [19.4973869, 48.3708814],
geometry: {
type: 'Point',
coordinates: [19.4973869, 48.3708814],
},
context: [
{
id: 'country.14296',
osm_id: 'relation14296',
text: 'Slovakia',
},
{
id: 'state.388270',
osm_id: 'relation388270',
text: 'Region of Banská Bystrica',
},
],
}
*/

const IconPart = styled.div`
width: 50px;
Expand Down Expand Up @@ -71,64 +65,43 @@ const getDistance = (point1, point2) => {
);
};

const join = (a, sep, b) => `${a || ''}${a && b ? sep : ''}${b || ''}`;

const useMapCenter = () => {
const {
view: [, lat, lon],
} = useMapStateContext();
return { lon, lat };
};

const highlightText = (resultText, inputValue) => {
const parts = parse(resultText, match(resultText, inputValue));
const map = parts.map((part, index) => (
// eslint-disable-next-line react/no-array-index-key
<span key={index} style={{ fontWeight: part.highlight ? 700 : 400 }}>
{part.text}
</span>
));
return map;
};

function getTextsFromAddress(address) {
const [, resultText] = Object.entries(address)[0];
const additionalText = join(
join(address.road, ', ', address.state),
', ',
address.country_code?.toUpperCase(),
);
return { resultText, additionalText };
}

export const renderOptionFactory =
(inputValue) =>
({ address, class: tagKey, type: tagValue, lon, lat }) => {
const mapCenter = useMapCenter();
const dist = getDistance(mapCenter, { lon, lat }) / 1000;
const distKm = dist < 10 ? Math.round(dist * 10) / 10 : Math.round(dist); // TODO save imperial to mapState and multiply 0.621371192
export const renderOptionFactory = () => (option) => {
const { center, text, context, place_type: placeType } = option;
const [lon, lat] = center;

const properties = getPoiClass({ [tagKey]: tagValue });
const mapCenter = useMapCenter();
const dist = getDistance(mapCenter, { lon, lat }) / 1000;
const distKm = dist < 10 ? Math.round(dist * 10) / 10 : Math.round(dist); // TODO save imperial to mapState and multiply 0.621371192

const { resultText, additionalText } = getTextsFromAddress(address);

return (
<>
<IconPart>
<Maki
ico={properties.class}
style={{ width: '20px', height: '20px', opacity: 0.5 }}
title={`${tagKey}=${tagValue}`}
/>
<div>{distKm} km</div>
</IconPart>
<Grid item xs>
{highlightText(resultText, inputValue)}

<Typography variant="body2" color="textSecondary">
{additionalText}
</Typography>
</Grid>
</>
);
};
const country =
context?.filter((x) => x.id?.split('.')[0] === 'country') ?? [];
const notCountry =
context?.filter((x) => x.id?.split('.')[0] !== 'country') ?? [];
const orderedContext = [...notCountry, ...country];
const additionalText = orderedContext?.map((x) => x.text).join(', ');
return (
<>
<IconPart>
<Maki
ico={placeType}
style={{ width: '20px', height: '20px', opacity: 0.5 }}
title={placeType}
/>
<div>{distKm} km</div>
</IconPart>
<Grid item xs>
{text}
<Typography variant="body2" color="textSecondary">
{additionalText}
</Typography>
</Grid>
</>
);
};
14 changes: 7 additions & 7 deletions src/services/intl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ export const setIntl = (initialIntl: Intl) => {
};

export const InjectIntl = ({ intl: globalIntl }) => (
<script
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: `var GLOBAL_INTL = ${JSON.stringify(globalIntl)};`,
}}
/>
);
<script
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: `var GLOBAL_INTL = ${JSON.stringify(globalIntl)};`,
}}
/>
);

if (isBrowser()) {
setIntl((window as any).GLOBAL_INTL);
Expand Down

0 comments on commit f0fa54e

Please sign in to comment.