Skip to content

Commit

Permalink
feat: add AOI modal and search bar in map
Browse files Browse the repository at this point in the history
Closes #122
  • Loading branch information
stdavis committed Apr 1, 2021
1 parent 91a779b commit 5bd7d8d
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 61 deletions.
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@fortawesome/react-fontawesome": "^0.1.14",
"bootstrap": "^4.6.0",
"downshift": "^6.1.0",
"lodash.throttle": "^4.1.1",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-error-boundary": "^3.1.1",
Expand Down
44 changes: 33 additions & 11 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import * as React from 'react';
import './App.css';
import { ErrorBoundary } from 'react-error-boundary';

import { Sherlock, LocatorSuggestProvider } from '@agrc/sherlock';
import * as React from 'react';
import AOIModal from './components/AOIModal';
import config from './config';
import Extent from '@arcgis/core/geometry/Extent';
import Graphic from '@arcgis/core/Graphic';
import Header from './components/Header';
import Map from './components/esrijs/Map';

import './App.css';
import persistMapExtent from './components/esrijs/persistMapExtent';

const ErrorFallback = ({ error }) => {
return (
Expand All @@ -16,22 +20,40 @@ const ErrorFallback = ({ error }) => {
};

export default function App() {
console.log('App render');
const onMapClick = React.useCallback((event) => {
console.log('onMapClick', event);
}, []);
const [mapView, setMapView] = React.useState(null);
const initialExtent = persistMapExtent(mapView);
const [zoomToExtent, setZoomToExtent] = React.useState(null);

return (
<div className="app">
<ErrorBoundary FallbackComponent={ErrorFallback}>
<Header title="Utah Residential Broadband" />
<Map
onClick={onMapClick}
setView={setMapView}
zoomToGraphic={null}
view={mapView}
webMapId={process.env.REACT_APP_WEB_MAP_ID}
/>
{initialExtent || zoomToExtent ? (
<Map
onClick={onMapClick}
setView={setMapView}
view={mapView}
webMapId={process.env.REACT_APP_WEB_MAP_ID}
initialExtent={initialExtent || zoomToExtent}
zoomToExtent={zoomToExtent}
>
{mapView ? (
<Sherlock
provider={new LocatorSuggestProvider(config.urls.masquerade, 3857)}
onSherlockMatch={(matches) => setZoomToExtent(new Extent(matches[0].attributes.extent))}
modules={{ Graphic }}
position="top-right"
mapView={mapView}
/>
) : null}
</Map>
) : (
<AOIModal setExtent={setZoomToExtent} />
)}
</ErrorBoundary>
</div>
);
Expand Down
34 changes: 34 additions & 0 deletions src/components/AOIModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react';
import propTypes from 'prop-types';
import { Sherlock, LocatorSuggestProvider } from '@agrc/sherlock';
import config from '../config';
import Graphic from '@arcgis/core/Graphic';
import Extent from '@arcgis/core/geometry/Extent';
import { Modal, ModalBody } from 'reactstrap';

const AOIModal = ({ setExtent }) => {
const onMatch = (graphics) => {
setExtent(new Extent(graphics[0].attributes.extent));
};

return (
<div>
<Modal isOpen={true}>
<ModalBody>
<p>What is your area of interest? Search for things like counties, cities, addresses, place names, etc...</p>
<Sherlock
provider={new LocatorSuggestProvider(config.urls.masquerade, 3857)}
modules={{ Graphic }}
onSherlockMatch={onMatch}
/>
</ModalBody>
</Modal>
</div>
);
};

AOIModal.propTypes = {
setExtent: propTypes.func.isRequired,
};

export default AOIModal;
2 changes: 1 addition & 1 deletion src/components/esrijs/Map.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.esri-view {
.map-container {
position: absolute;
top: 75px;
bottom: 0;
Expand Down
69 changes: 21 additions & 48 deletions src/components/esrijs/Map.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import React, { useRef, useEffect } from 'react';
import MapView from '@arcgis/core/views/MapView';
import WebMap from '@arcgis/core/WebMap';
import { once } from '@arcgis/core/core/watchUtils';
import './Map.css';
import addMapPropsToGlobal from './addMapPropsToGlobal';
import propTypes from 'prop-types';
import './Map.css';
import React, { useRef, useEffect } from 'react';
import WebMap from '@arcgis/core/WebMap';
import MapView from '@arcgis/core/views/MapView';

const Map = ({ onClick, setView, zoomToGraphic, view, webMapId }) => {
const Map = ({ onClick, setView, view, webMapId, children, zoomToExtent, initialExtent }) => {
const mapDiv = useRef(null);
const displayedZoomGraphic = useRef(null);

useEffect(() => {
if (!mapDiv.current) {
if (!mapDiv.current || view) {
return;
}
console.log('map init');

const map = new WebMap({ portalItem: { id: webMapId } });
const mapView = new MapView({
extent: initialExtent,
container: mapDiv.current,
map,
ui: {
components: ['zoom'],
},
constraints: {
snapToZoom: true,
},
});

if (window.Cypress) {
Expand All @@ -32,56 +34,27 @@ const Map = ({ onClick, setView, zoomToGraphic, view, webMapId }) => {
mapView.on('click', onClick);

setView(mapView);
}, [onClick, setView, webMapId]);
}, [initialExtent, onClick, setView, view, webMapId]);

useEffect(() => {
if (!zoomToGraphic?.graphic) {
return;
}

if (!Array.isArray(zoomToGraphic.graphic)) {
zoomToGraphic.graphic = [zoomToGraphic.graphic];
}

let zoom;
if (!zoomToGraphic.zoom) {
if (zoomToGraphic.graphic.every((graphic) => graphic.geometry.type === 'point')) {
zoom = {
target: zoomToGraphic.graphic,
zoom: view.map.basemap.baseLayers.items[0].tileInfo.lods.length - 5,
};
} else {
zoom = {
target: zoomToGraphic.graphic,
};
}
}
if (!zoomToExtent || !view) return;

if (displayedZoomGraphic.current) {
view.graphics.removeMany(displayedZoomGraphic.current);
}

displayedZoomGraphic.current = zoom.target;

view.graphics.addMany(zoom.target);

view.goTo(zoom).then(() => {
if (!zoom.preserve) {
once(view, 'extent', () => {
view.graphics.removeAll();
});
}
});
}, [zoomToGraphic, view]);
view.goTo(zoomToExtent);
}, [zoomToExtent, view]);

return <div ref={mapDiv}></div>;
return (
<div className="map-container" ref={mapDiv}>
{children}
</div>
);
};
Map.propTypes = {
onClick: propTypes.func.isRequired,
setView: propTypes.func.isRequired,
zoomToGraphic: propTypes.object,
view: propTypes.instanceOf(MapView),
webMapId: propTypes.string,
zoomToExtent: propTypes.object,
initialExtent: propTypes.object,
};

export default Map;
23 changes: 23 additions & 0 deletions src/components/esrijs/persistMapExtent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import throttle from 'lodash.throttle';

const key = 'broadband:map-extent';
let wired = false;

export default function persistMapExtent(mapView) {
if (mapView && !wired) {
mapView.watch(
'extent',
throttle(() => {
localStorage.setItem(key, JSON.stringify(mapView.extent));
}, 1000)
);

wired = true;
}

const item = localStorage.getItem(key);

if (item) {
return JSON.parse(item);
}
}
6 changes: 5 additions & 1 deletion src/config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
const config = {};
const config = {
urls: {
masquerade: 'https://masquerade-kkktr623oa-uc.a.run.app/arcgis/rest/services/UtahLocator/GeocodeServer',
},
};

export default config;

0 comments on commit 5bd7d8d

Please sign in to comment.