Skip to content

Commit

Permalink
Update map search to use autosuggest and geocoding (#140)
Browse files Browse the repository at this point in the history
In the previous version of the map search, the crossings visible on the map would simply be filtered out by the search query. This update changes that so:
* Setting focus on the search bar replaces the nearby crossings with suggestions
  * These suggestions include Crossings, Communities, and Geocoded locations
* Selecting a suggested crossing:
  * Centers the map and zooms in on the crossing
  * Shows the crossing details in the sidebar
* Selecting a suggested community:
  * Filters the map to only show crossings in that community
  * Sets the map bounds to fit the community
* Selecting a geocoded location
  * Centers the map and zooms in on the location

Libraries used:
* [react-autosuggest](https://github.com/moroshko/react-autosuggest)
* [mapbox-sdk-js](https://github.com/mapbox/mapbox-sdk-js) (for geocoding)
  • Loading branch information
Brian Smith committed Feb 28, 2018
1 parent ed65213 commit 206035d
Show file tree
Hide file tree
Showing 18 changed files with 744 additions and 220 deletions.
2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"formatcoords": "^1.1.3",
"get-graphql-schema": "^2.1.0",
"jwt-decode": "^2.2.0",
"mapbox": "^1.0.0-beta9",
"mapbox-gl": "^0.40.0",
"mobile-detect": "^1.4.1",
"moment": "^2.18.1",
Expand All @@ -24,6 +25,7 @@
"prop-types": "^15.6.0",
"react": "^16.0.0-rc.3",
"react-apollo": "^1.4.2",
"react-autosuggest": "^9.3.3",
"react-container-query": "^0.9.1",
"react-csv": "^1.0.8",
"react-dom": "^16.0.0-rc.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ class CrossingListItem extends React.Component {
<div className="CrossingListItemFlexContainer">
<div className="CrossingListItemFlexItem">
<div className="ControlLabel">
Status: {statusConstants.strings[this.state.selectedStatus]}
Status: {statusConstants.statusNames[this.state.selectedStatus]}
</div>
<StatusToggle
status={this.state.selectedStatus}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { Component } from 'react';
import { get } from 'lodash';
import { displayedInputs, statusIcons } from 'constants/StatusConstants';
import { displayedInputs } from 'constants/StatusConstants';
import DateTime from 'components/Dashboard/CrossingListPage/CrossingListItem/DateTime';
import StatusIcon from 'components/Shared/StatusIcon';
import 'components/Dashboard/CrossingStatusHistory/CrossingStatusHistory.css';

class CrossingStatusHistoryItem extends Component {
Expand Down Expand Up @@ -42,9 +43,9 @@ class CrossingStatusHistoryItem extends Component {

<div className="CrossingStatusHistory__item-details-flexcontainer">
<div className="CrossingStatusHistory__item-details">
<img
<StatusIcon
statusId={statusId}
onLoad={measure}
src={statusIcons[statusId]}
alt={shouldDisplay.status}
className="CrossingStatusHistory__status-icon"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class PublicCrossingListItem extends React.Component {
<div className="CrossingListItemFlexContainer">
<div className="CrossingListItemFlexItem">
<div className="ControlLabel">
Status: {statusConstants.strings[crossing.latestStatusId]}
Status: {statusConstants.statusNames[crossing.latestStatusId]}
</div>
</div>

Expand Down
92 changes: 72 additions & 20 deletions frontend/src/components/Shared/CrossingMapPage/CrossingMapPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,13 @@ class CrossingMapPage extends Component {
super(props);

// If we have a current user, we're on the dashboard, we should get their viewport
const envelope = this.props.currentUser
? JSON.parse(
this.props.currentUser.communityByCommunityId.viewportgeojson,
)
: JSON.parse(
`{"type":"Polygon","coordinates":[[[-98.086914,30.148464],[-98.086914,30.433285],[-97.615974,30.433285],[-97.615974,30.148464],[-98.086914,30.148464]]]}`,
);

// I actually like doing the padding here because it keeps the data/view separation
const viewport = [
[
Math.min(...envelope.coordinates[0].map(arr => arr[0])) - 0.1,
Math.min(...envelope.coordinates[0].map(arr => arr[1])) - 0.1,
],
[
Math.max(...envelope.coordinates[0].map(arr => arr[0])) + 0.1,
Math.max(...envelope.coordinates[0].map(arr => arr[1])) + 0.1,
],
];
const viewportgeojson = this.props.currentUser
? this.props.currentUser.communityByCommunityId.viewportgeojson
: `{"type":"Polygon","coordinates":[[[-98.086914,30.148464],[-98.086914,30.433285],[-97.615974,30.433285],[-97.615974,30.148464],[-98.086914,30.148464]]]}`;

const viewportAndCenter = this.getViewportAndCenter(viewportgeojson);

this.state = {
viewport: viewport,
selectedCrossingId: null,
selectedCrossingStatus: null,
fullscreen: false,
Expand All @@ -55,9 +40,39 @@ class CrossingMapPage extends Component {
showCaution: true,
showLongterm: true,
visibleCrossings: [],
selectedLocationCoordinates: null,
selectedCommunity: null,
viewport: viewportAndCenter.viewport,
center: viewportAndCenter.center,
mapCenter: viewportAndCenter.center,
};
}

getViewportAndCenter = viewportgeojson => {
const envelope = JSON.parse(viewportgeojson);

const viewport = [
[
Math.min(...envelope.coordinates[0].map(arr => arr[0])) - 0.1,
Math.min(...envelope.coordinates[0].map(arr => arr[1])) - 0.1,
],
[
Math.max(...envelope.coordinates[0].map(arr => arr[0])) + 0.1,
Math.max(...envelope.coordinates[0].map(arr => arr[1])) + 0.1,
],
];

const center = {
lng: (viewport[0][0] + viewport[1][0]) / 2,
lat: (viewport[0][1] + viewport[1][1]) / 2,
};

return {
viewport: viewport,
center: center,
};
};

formatSearchQuery(query) {
return `%${query.replace(/ /g, '%')}%`;
}
Expand Down Expand Up @@ -98,15 +113,40 @@ class CrossingMapPage extends Component {
this.setState({ showLongterm: !this.state.showLongterm });
};

getMapCenter = center => {
this.setState({ mapCenter: center });
};

setSelectedLocationCoordinates = coordinates => {
this.setState({ selectedLocationCoordinates: coordinates });
};

setSelectedCommunity = community => {
this.setState({ selectedCommunity: community });
if (community && community.viewportgeojson) {
const viewportAndCenter = this.getViewportAndCenter(
community.viewportgeojson,
);
this.setState({
viewport: viewportAndCenter.viewport,
center: viewportAndCenter.center,
mapCenter: viewportAndCenter.mapCenter,
});
}
};

render() {
const {
viewport,
mapCenter,
selectedCrossingId,
selectedCrossingStatus,
searchQuery,
formattedSearchQuery,
visibleCrossings,
selectedCrossingName,
selectedLocationCoordinates,
selectedCommunity,
} = this.state;
const { currentUser } = this.props;
const allCommunities =
Expand All @@ -132,6 +172,7 @@ class CrossingMapPage extends Component {
searchQuery={searchQuery}
searchQueryUpdated={this.searchQueryUpdated}
selectedCrossingName={selectedCrossingName}
setSelectedCommunity={this.setSelectedCommunity}
/>
)}
{params.fullsize && (
Expand Down Expand Up @@ -162,6 +203,11 @@ class CrossingMapPage extends Component {
toggleShowLongterm={this.toggleShowLongterm}
visibleCrossings={visibleCrossings}
allCommunities={allCommunities}
center={mapCenter}
setSelectedLocationCoordinates={
this.setSelectedLocationCoordinates
}
setSelectedCommunity={this.setSelectedCommunity}
/>
)}
<div
Expand All @@ -174,6 +220,8 @@ class CrossingMapPage extends Component {
mapHeight="100%"
mapWidth="100%"
viewport={viewport}
getMapCenter={this.getMapCenter}
selectedLocationCoordinates={selectedLocationCoordinates}
selectedCrossingId={selectedCrossingId}
selectedCrossingStatus={selectedCrossingStatus}
selectCrossing={this.selectCrossing}
Expand All @@ -184,6 +232,9 @@ class CrossingMapPage extends Component {
showCaution={this.state.showCaution}
showLongterm={this.state.showLongterm}
setVisibleCrossings={this.setVisibleCrossings}
selectedCommunityId={
selectedCommunity && selectedCommunity.id
}
/>
</div>
{!params.fullsize &&
Expand Down Expand Up @@ -211,6 +262,7 @@ const allCommunities = gql`
nodes {
id
name
viewportgeojson
}
}
}
Expand Down
Loading

0 comments on commit 206035d

Please sign in to comment.