mirror of
https://github.com/visgl/react-map-gl.git
synced 2026-01-18 15:54:22 +00:00
Add geocoder example (#1690)
This commit is contained in:
parent
262ea3f126
commit
aa910e2c67
12
examples/geocoder/README.md
Normal file
12
examples/geocoder/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Example: Geocoder
|
||||
|
||||
This app reproduces Mapbox's [Add a geocoder](https://docs.mapbox.com/mapbox-gl-js/example/mapbox-gl-geocoder/) example.
|
||||
|
||||
## Usage
|
||||
|
||||
To run this example, you need a [Mapbox token](http://visgl.github.io/react-map-gl/docs/get-started/mapbox-tokens). You can either set it as `MAPBOX_TOKEN` in `src/app.js`, or set a `MapboxAccessToken` environment variable in the command line.
|
||||
|
||||
```bash
|
||||
npm i
|
||||
npm run start
|
||||
```
|
||||
41
examples/geocoder/index.html
Normal file
41
examples/geocoder/index.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='UTF-8' />
|
||||
<title>react-map-gl Example</title>
|
||||
<link href="https://api.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v4.7.2/mapbox-gl-geocoder.css" type="text/css">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
}
|
||||
#map {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
.control-panel {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
max-width: 320px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
||||
padding: 12px 24px;
|
||||
margin: 20px;
|
||||
font-size: 13px;
|
||||
line-height: 2;
|
||||
color: #6b6b76;
|
||||
text-transform: uppercase;
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src='app.js'></script>
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
App.renderToDom(document.getElementById('map'));
|
||||
</script>
|
||||
</html>
|
||||
24
examples/geocoder/package.json
Normal file
24
examples/geocoder/package.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --progress --hot --open",
|
||||
"start-local": "webpack-dev-server --env local --progress --hot --open"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mapbox/mapbox-gl-geocoder": "^4.7.4",
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0",
|
||||
"react-map-gl": "^7.0.0-alpha.2",
|
||||
"mapbox-gl": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/preset-env": "^7.0.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"babel-loader": "^8.0.0",
|
||||
"ts-loader": "^9.0.0",
|
||||
"typescript": "^4.0.0",
|
||||
"webpack": "^5.65.0",
|
||||
"webpack-cli": "^4.9.0",
|
||||
"webpack-dev-server": "^4.7.0"
|
||||
}
|
||||
}
|
||||
31
examples/geocoder/src/app.tsx
Normal file
31
examples/geocoder/src/app.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import * as React from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import Map from 'react-map-gl';
|
||||
|
||||
import GeocoderControl from './geocoder-control';
|
||||
import ControlPanel from './control-panel';
|
||||
|
||||
const TOKEN = process.env.MapboxAccessToken; // Set your mapbox token here
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<>
|
||||
<Map
|
||||
initialViewState={{
|
||||
longitude: -79.4512,
|
||||
latitude: 43.6568,
|
||||
zoom: 13
|
||||
}}
|
||||
mapStyle="mapbox://styles/mapbox/streets-v9"
|
||||
mapboxAccessToken={TOKEN}
|
||||
>
|
||||
<GeocoderControl mapboxAccessToken={TOKEN} position="top-left" />
|
||||
<ControlPanel />
|
||||
</Map>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function renderToDom(container) {
|
||||
render(<App />, container);
|
||||
}
|
||||
19
examples/geocoder/src/control-panel.tsx
Normal file
19
examples/geocoder/src/control-panel.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import * as React from 'react';
|
||||
|
||||
function ControlPanel(props) {
|
||||
return (
|
||||
<div className="control-panel">
|
||||
<h3>Geocoder</h3>
|
||||
<div className="source-link">
|
||||
<a
|
||||
href="https://github.com/visgl/react-map-gl/tree/6.0-release/examples/geocoder"
|
||||
target="_new"
|
||||
>
|
||||
View Code ↗
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(ControlPanel);
|
||||
142
examples/geocoder/src/geocoder-control.tsx
Normal file
142
examples/geocoder/src/geocoder-control.tsx
Normal file
@ -0,0 +1,142 @@
|
||||
import * as React from 'react';
|
||||
import {useState} from 'react';
|
||||
import {useControl, Marker, ControlPosition} from 'react-map-gl';
|
||||
import MapboxGeocoder from '@mapbox/mapbox-gl-geocoder';
|
||||
|
||||
type GeocoderControlProps = {
|
||||
mapboxAccessToken: string;
|
||||
origin?: string;
|
||||
zoom?: number;
|
||||
flyTo?: boolean | object;
|
||||
placeholder?: string;
|
||||
proximity?: {
|
||||
longitude: number;
|
||||
latitude: number;
|
||||
};
|
||||
trackProximity?: boolean;
|
||||
collapsed?: boolean;
|
||||
clearAndBlurOnEsc?: boolean;
|
||||
clearOnBlur?: boolean;
|
||||
box?: [number, number, number, number];
|
||||
countries?: string;
|
||||
types?: string;
|
||||
minLength?: number;
|
||||
limit?: number;
|
||||
language?: string;
|
||||
filter?: (feature: object) => boolean;
|
||||
localGeocoder?: Function;
|
||||
externalGeocoder?: Function;
|
||||
reverseMode?: 'distance' | 'score';
|
||||
reverseGeocode?: boolean;
|
||||
enableEventLogging?: boolean;
|
||||
marker?: boolean | object;
|
||||
render?: (feature: object) => string;
|
||||
getItemValue?: (feature: object) => string;
|
||||
mode?: 'mapbox.places' | 'mapbox.places-permanent';
|
||||
localGeocoderOnly?: boolean;
|
||||
autocomplete?: boolean;
|
||||
fuzzyMatch?: boolean;
|
||||
routing?: boolean;
|
||||
worldview?: string;
|
||||
|
||||
position: ControlPosition;
|
||||
|
||||
onLoading?: (e: object) => void;
|
||||
onResults?: (e: object) => void;
|
||||
onResult?: (e: object) => void;
|
||||
onError?: (e: object) => void;
|
||||
};
|
||||
|
||||
export default function GeocoderControl(props: GeocoderControlProps) {
|
||||
const [marker, setMarker] = useState(null);
|
||||
|
||||
const geocoder: any = useControl(
|
||||
() => {
|
||||
const ctrl = new MapboxGeocoder({
|
||||
...props,
|
||||
accessToken: props.mapboxAccessToken
|
||||
});
|
||||
ctrl.on('loading', props.onLoading);
|
||||
ctrl.on('results', props.onResults);
|
||||
ctrl.on('result', evt => {
|
||||
props.onResult(evt);
|
||||
|
||||
const {result} = evt;
|
||||
const location =
|
||||
result &&
|
||||
(result.center || (result.geometry?.type === 'Point' && result.geometry.coordinates));
|
||||
if (location) {
|
||||
setMarker(<Marker {...props.marker} longitude={location[0]} latitude={location[1]} />);
|
||||
} else {
|
||||
setMarker(null);
|
||||
}
|
||||
});
|
||||
ctrl.on('error', props.onError);
|
||||
return ctrl;
|
||||
},
|
||||
{
|
||||
position: props.position
|
||||
}
|
||||
);
|
||||
|
||||
if (geocoder._map) {
|
||||
if (geocoder.getProximity() !== props.proximity && props.proximity !== undefined) {
|
||||
geocoder.setProximity(props.proximity);
|
||||
}
|
||||
if (geocoder.getRenderFunction() !== props.render && props.render !== undefined) {
|
||||
geocoder.setRenderFunction(props.render);
|
||||
}
|
||||
if (geocoder.getLanguage() !== props.language && props.language !== undefined) {
|
||||
geocoder.setLanguage(props.language);
|
||||
}
|
||||
if (geocoder.getZoom() !== props.zoom && props.zoom !== undefined) {
|
||||
geocoder.setZoom(props.zoom);
|
||||
}
|
||||
if (geocoder.getFlyTo() !== props.flyTo && props.flyTo !== undefined) {
|
||||
geocoder.setFlyTo(props.zoom);
|
||||
}
|
||||
if (geocoder.getPlaceholder() !== props.placeholder && props.placeholder !== undefined) {
|
||||
geocoder.setPlaceholder(props.zoom);
|
||||
}
|
||||
if (geocoder.getCountries() !== props.countries && props.countries !== undefined) {
|
||||
geocoder.setCountries(props.zoom);
|
||||
}
|
||||
if (geocoder.getTypes() !== props.types && props.types !== undefined) {
|
||||
geocoder.setTypes(props.zoom);
|
||||
}
|
||||
if (geocoder.getMinLength() !== props.minLength && props.minLength !== undefined) {
|
||||
geocoder.setMinLength(props.zoom);
|
||||
}
|
||||
if (geocoder.getLimit() !== props.limit && props.limit !== undefined) {
|
||||
geocoder.setLimit(props.zoom);
|
||||
}
|
||||
if (geocoder.getFilter() !== props.filter && props.filter !== undefined) {
|
||||
geocoder.setFilter(props.zoom);
|
||||
}
|
||||
if (geocoder.getOrigin() !== props.origin && props.origin !== undefined) {
|
||||
geocoder.setOrigin(props.zoom);
|
||||
}
|
||||
if (geocoder.getAutocomplete() !== props.autocomplete && props.autocomplete !== undefined) {
|
||||
geocoder.setAutocomplete(props.zoom);
|
||||
}
|
||||
if (geocoder.getFuzzyMatch() !== props.fuzzyMatch && props.fuzzyMatch !== undefined) {
|
||||
geocoder.setFuzzyMatch(props.zoom);
|
||||
}
|
||||
if (geocoder.getRouting() !== props.routing && props.routing !== undefined) {
|
||||
geocoder.setRouting(props.zoom);
|
||||
}
|
||||
if (geocoder.getWorldview() !== props.worldview && props.worldview !== undefined) {
|
||||
geocoder.setWorldview(props.zoom);
|
||||
}
|
||||
}
|
||||
return marker;
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
GeocoderControl.defaultProps = {
|
||||
onLoading: noop,
|
||||
onResults: noop,
|
||||
onResult: noop,
|
||||
onError: noop
|
||||
};
|
||||
10
examples/geocoder/tsconfig.json
Normal file
10
examples/geocoder/tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"jsx": "react",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true
|
||||
}
|
||||
}
|
||||
54
examples/geocoder/webpack.config.js
Normal file
54
examples/geocoder/webpack.config.js
Normal file
@ -0,0 +1,54 @@
|
||||
// NOTE: To use this example standalone (e.g. outside of repo)
|
||||
// delete the local development overrides at the bottom of this file
|
||||
|
||||
// avoid destructuring for older Node version support
|
||||
const resolve = require('path').resolve;
|
||||
const webpack = require('webpack');
|
||||
|
||||
const config = {
|
||||
mode: 'development',
|
||||
|
||||
devServer: {
|
||||
static: '.'
|
||||
},
|
||||
|
||||
entry: {
|
||||
app: resolve('./src/app')
|
||||
},
|
||||
|
||||
output: {
|
||||
library: 'App'
|
||||
},
|
||||
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js', '.json']
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|js)x?$/,
|
||||
include: [resolve('.')],
|
||||
exclude: [/node_modules/],
|
||||
use: [
|
||||
{
|
||||
loader: 'babel-loader',
|
||||
options: {
|
||||
presets: ['@babel/env', '@babel/react']
|
||||
}
|
||||
},
|
||||
{
|
||||
loader: 'ts-loader'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// Optional: Enables reading mapbox token from environment variable
|
||||
plugins: [new webpack.EnvironmentPlugin({MapboxAccessToken: ''})]
|
||||
};
|
||||
|
||||
// Enables bundling against src in this repo rather than the installed version
|
||||
module.exports = env =>
|
||||
env && env.local ? require('../webpack.config.local')(config)(env) : config;
|
||||
Loading…
x
Reference in New Issue
Block a user