Skip to content

Commit

Permalink
Add geocoding requests with mapbox
Browse files Browse the repository at this point in the history
Signed-off-by: Francis Giraldeau <[email protected]>
  • Loading branch information
giraldeau committed Dec 16, 2016
1 parent 34589ff commit bbb2598
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 14 deletions.
5 changes: 4 additions & 1 deletion react-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
},
"dependencies": {
"immutable": "^3.8.1",
"promise-polyfill": "^6.0.2",
"react": "^15.4.1",
"react-autobind": "^1.0.6",
"react-dom": "^15.4.1",
"react-redux": "^4.4.6",
"react-simple-range": "^1.4.0",
"redux": "^3.6.0",
"semantic-ui-css": "^2.2.4",
"semantic-ui-react": "^0.61.4"
"semantic-ui-react": "^0.61.4",
"whatwg-fetch": "^2.0.1"
},
"scripts": {
"start": "react-scripts start",
Expand Down
7 changes: 6 additions & 1 deletion react-client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import ParametersPannel from './ParametersPannel.js';
import CarSelector from './CarSelector.js';
import Geocoder from './Geocoder.js'

const accessToken = '';

const carParameters = [
{ id: "battery",
label: "Battery capacity",
Expand Down Expand Up @@ -36,7 +38,10 @@ class App extends Component {
render() {
return (
<div className="App">
<Geocoder />
<Geocoder
accessToken={accessToken}
onSelect={(event) => { console.log(event); }}
/>
<CarSelector />
<ParametersPannel
parameters={carParameters}/>
Expand Down
154 changes: 142 additions & 12 deletions react-client/src/Geocoder.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,36 @@
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { Input } from 'semantic-ui-react'
import { List, Input } from 'semantic-ui-react'
import autoBind from 'react-autobind';

class Geocoder extends Component {
constructor() {
super();
autoBind(this);
this.state = this.getDefaultState();
}


search(endpoint, source, accessToken, proximity, bbox, types, query, callback) {
var searchTime = new Date();
var uri = endpoint + '/geocoding/v5/' +
source + '/' + encodeURIComponent(query) + '.json' +
'?access_token=' + accessToken +
(proximity ? '&proximity=' + proximity : '') +
(bbox ? '&bbox=' + bbox : '') +
(types ? '&types=' + encodeURIComponent(types) : '');
fetch(uri, {
headers: {
Accept: 'application/json',
},
}).then(function(response) {
return response.json();
}).then(function(json) {
callback(json, searchTime);
}).catch(function(ex) {
console.log("mapbox geocoding search error", ex);
});
}

getDefaultState() {
return {
results: [],
Expand All @@ -22,20 +45,127 @@ class Geocoder extends Component {
ReactDOM.findDOMNode(this.refs.input).focus();
}

onInput(event) {
this.setState({loading: true});
var value = event.target.value;
if (value === '') {
this.setState(this.getDefaultState());
} else {
this.search(
this.props.endpoint,
this.props.source,
this.props.accessToken,
this.props.proximity,
this.props.bbox,
this.props.types,
value,
this.onResult);
}
}

moveFocus(dir) {
if (this.state.loading)
return;

var focus = 0;
if (this.state.focus !== null) {
focus = Math.max(0, Math.min(this.state.results.length - 1, this.state.focus + dir))
}
console.log("focus", focus);
this.setState({
focus: focus,
});
}

acceptFocus() {
if (this.state.focus !== null) {
this.props.onSelect(this.state.results[this.state.focus]);
}
}

onKeyDown(event) {
switch (event.which) {
// up
case 38:
event.preventDefault();
this.moveFocus(-1);
break;
// down
case 40:
this.moveFocus(1);
break;
// accept
case 13:
if (this.state.results.length > 0 && this.state.focus == null) {
this.clickOption(this.state.results[0], 0);
}
this.acceptFocus();
break;
default:
break;
}
}

onResult(body, searchTime) {
// searchTime is compared with the last search to set the state
// to ensure that a slow xhr response does not scramble the
// sequence of autocomplete display.
if (body && body.features && this.state.searchTime <= searchTime) {
this.setState({
searchTime: searchTime,
loading: false,
results: body.features,
focus: null
});
this.props.onSuggest(this.state.results);
}
}

clickOption(place, listLocation) {
this.props.onSelect(place);
this.setState({focus: listLocation});
// focus on the input after click to maintain key traversal
ReactDOM.findDOMNode(this.refs.input).focus();
return false;
}

render() {
var resultList = null;
if (this.state.results.length > 0) {
const items = this.state.results.map((result, i) => {
return (
<List.Item key={result.id}>
<List.Content>
<a href="#"
onClick={this.clickOption.bind(this, result, i)}>
{result.place_name}
</a>
</List.Content>
</List.Item>
);
});

resultList = (
<List selection verticalAlign='middle'>
{items}
</List>
);
}

return (
<Input
fluid
ref="input"
icon='location arrow'
iconPosition='left'
placeholder='Departure'
onChange={(event) => {
console.log(event.target.value);
}}
/>
<div>
<Input
fluid
ref="input"
icon='location arrow'
iconPosition='left'
placeholder={this.props.inputPlaceholder}
onInput={this.onInput}
onKeyDown={this.onKeyDown}
type='text'
/>
{resultList}
</div>
);
}
}
Expand Down
7 changes: 7 additions & 0 deletions react-client/src/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import 'whatwg-fetch'
import Promise from 'promise-polyfill';
import './index.css';

// To add to window
if (!window.Promise) {
window.Promise = Promise;
}

ReactDOM.render(
<App />,
document.getElementById('root')
Expand Down
Empty file added react-client/webpack.config.js
Empty file.

0 comments on commit bbb2598

Please sign in to comment.