Skip to content

Commit

Permalink
Merge pull request #3580 from Vizzuality/feat/recent-imagery
Browse files Browse the repository at this point in the history
Feat/recent imagery
  • Loading branch information
edbrett committed Sep 11, 2018
2 parents 82cefc7 + 132e1ac commit 62a386f
Show file tree
Hide file tree
Showing 57 changed files with 1,163 additions and 1,117 deletions.
2 changes: 1 addition & 1 deletion app/javascript/assets/icons/satellite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 2 additions & 3 deletions app/javascript/components/map-v2/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ export const setMapSettings = createThunkAction(
export const setLandsatBasemap = createThunkAction(
'setLandsatBasemap',
(year, defaultUrl) => (dispatch, getState) => {
const { location } = getState();
const mapZoom = getMapZoom(location);
const currentBasemap = getBasemap(location);
const mapZoom = getMapZoom(getState());
const currentBasemap = getBasemap(getState());
const landsat = {
key: `GFW__GEE_LANDSAT_BASEMAP_URL_${year}`,
get geeUrl() {
Expand Down
242 changes: 173 additions & 69 deletions app/javascript/components/map-v2/component.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import React, { PureComponent, Fragment } from 'react';
import PropTypes from 'prop-types';
import isEmpty from 'lodash/isEmpty';
import startCase from 'lodash/startCase';
import upperFirst from 'lodash/upperFirst';
import moment from 'moment';
import { format } from 'd3-format';

import Map from 'wri-api-components/dist/map';
import HTML5Backend from 'react-dnd-html5-backend';
import { DragDropContextProvider } from 'react-dnd';

import { LayerManager, Layer } from 'layer-manager/dist/react';
import { PluginLeaflet } from 'layer-manager';

import { Tooltip } from 'react-tippy';
import Tip from 'components/ui/tip';
import Loader from 'components/ui/loader';
import NoContent from 'components/ui/no-content';
import Icon from 'components/ui/icon';
Expand All @@ -17,18 +21,38 @@ import iconCross from 'assets/icons/close.svg';
import Popup from './components/popup';
import MapControlButtons from './components/map-controls';
import MapAttributions from './components/map-attributions';
import RecentImagery from './components/recent-imagery';

import './styles.scss';

class MapComponent extends PureComponent {
state = {
showTooltip: false,
tooltipData: {}
};

componentDidMount() {
requestAnimationFrame(() => {
this.map.invalidateSize();
L.control.scale({ maxWidth: 80 }).addTo(this.map); // eslint-disable-line
});
}

renderTooltip = data => (
<div>
{Object.keys(data).map(key => (
<p key={key}>
<strong>{upperFirst(startCase(key).toLowerCase())}</strong>:{' '}
{data[key]}
</p>
))}
<p className="view-more">Click to view more.</p>
</div>
);

handleShowTooltip = (show, data) => {
this.setState({ showTooltip: show, tooltipData: data });
};

render() {
const {
loading,
Expand All @@ -39,77 +63,153 @@ class MapComponent extends PureComponent {
label,
setMapSettings,
bbox,
recentImagery,
setInteraction
geostore,
setInteraction,
tileGeoJSON,
setRecentImagerySettings
} = this.props;

return (
<Fragment>
<Map
customClass="c-map"
onReady={map => {
this.map = map;
}}
mapOptions={mapOptions}
basemap={basemap}
label={label}
bounds={bbox}
events={{
zoomend: (e, map) => {
setMapSettings({ zoom: map.getZoom() });
},
dragend: (e, map) => {
setMapSettings({ center: map.getCenter() });
}
}}
<Tooltip
theme="tip"
hideOnClick
html={
<Tip
className="map-hover-tooltip"
text={this.renderTooltip(this.state.tooltipData)}
/>
}
position="top"
followCursor
animateFill={false}
open={this.state.showTooltip}
>
{map => (
<Fragment>
<LayerManager map={map} plugin={PluginLeaflet}>
{layerManager =>
activeLayers.map(l => {
const { interactionConfig, isBoundary } = l;
const { output, article } = interactionConfig || {};
const layer = {
...l,
...(!isEmpty(output) && {
interactivity: output.map(i => i.column),
events: {
click: e => {
setInteraction({
...e,
label: l.name,
article,
isBoundary,
id: l.id,
value: l.id,
config: output
});
}
}
})
};
<Map
customClass="c-map"
onReady={map => {
this.map = map;
}}
mapOptions={mapOptions}
basemap={basemap}
label={label}
bounds={bbox}
events={{
moveend: (e, map) => {
setMapSettings({
zoom: map.getZoom(),
center: map.getCenter()
});
}
}}
>
{map => (
<Fragment>
<LayerManager map={map} plugin={PluginLeaflet}>
{layerManager => (
<Fragment>
{geostore &&
geostore.id && (
<Layer
id={geostore.id}
name="Geojson"
provider="leaflet"
layerConfig={{
type: 'geoJSON',
body: geostore.geojson,
options: {
style: {
stroke: true,
color: '#4a4a4a',
weight: 2,
fill: false
}
}
}}
layerManager={layerManager}
/>
)}
{tileGeoJSON && (
<Layer
id="recentImagery"
name="Geojson"
provider="leaflet"
layerConfig={{
type: 'geoJSON',
body: tileGeoJSON,
options: {
style: {
stroke: false,
fillOpacity: 0
}
}
}}
layerManager={layerManager}
// Interaction
interactivity
events={{
click: () => {
setRecentImagerySettings({ visible: true });
},
mouseover: e => {
const data = e.layer.feature.properties;
const { cloudScore, instrument, dateTime } = data;
this.handleShowTooltip(true, {
instrument: startCase(instrument),
date: moment(dateTime)
.format('DD MMM YYYY, HH:mm')
.toUpperCase(),
cloudCoverage: `${format('.0f')(cloudScore)}%`
});
},
mouseout: () => {
this.handleShowTooltip(false, {});
}
}}
/>
)}
{activeLayers.map(l => {
const { interactionConfig, isBoundary } = l;
const { output, article } = interactionConfig || {};
const layer = {
...l,
...(!isEmpty(output) && {
interactivity: output.map(i => i.column),
events: {
click: e => {
if (!this.state.showTooltip) {
setInteraction({
...e,
label: l.name,
article,
isBoundary,
id: l.id,
value: l.id,
config: output
});
}
}
}
})
};

return (
<Layer
key={l.id}
{...layer}
layerManager={layerManager}
/>
);
})
}
</LayerManager>
<Popup map={map} />
<MapControlButtons className="map-controls" map={map} />
{recentImagery && (
<DragDropContextProvider backend={HTML5Backend}>
<RecentImagery map={map} />
</DragDropContextProvider>
)}
</Fragment>
)}
</Map>
return (
<Layer
key={l.id}
{...layer}
layerManager={layerManager}
/>
);
})}
</Fragment>
)}
</LayerManager>
<Popup map={map} />
<MapControlButtons className="map-controls" map={map} />
</Fragment>
)}
</Map>
</Tooltip>
<Icon className="icon-crosshair" icon={iconCross} />
<MapAttributions className="map-attributions" />
{loading && (
Expand All @@ -134,7 +234,11 @@ MapComponent.propTypes = {
setMapSettings: PropTypes.func,
setInteraction: PropTypes.func,
bbox: PropTypes.object,
recentImagery: PropTypes.bool
recentImagery: PropTypes.bool,
recentTileBounds: PropTypes.array,
setRecentImagerySettings: PropTypes.func,
geostore: PropTypes.object,
tileGeoJSON: PropTypes.object
};

export default MapComponent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createSelector, createStructuredSelector } from 'reselect';

import {
getBasemap,
getLabels,
getActiveDatasetsState,
getMapZoom,
getActiveBoundaryDatasets,
getBoundaryDatasets
} from 'components/map-v2/selectors';

import basemaps, { labels } from './basemaps-schema';

export const getAllBoundaries = createSelector(
[getBoundaryDatasets],
boundaries => [{ label: 'No boundaries', value: null }].concat(boundaries)
);

export const getBasemapsProps = createStructuredSelector({
activeDatasets: getActiveDatasetsState,
mapZoom: getMapZoom,
activeLabels: getLabels,
activeBasemap: getBasemap,
boundaries: getAllBoundaries,
activeBoundaries: getActiveBoundaryDatasets,
basemaps: () => basemaps,
labels: () => labels,
landsatYears: () =>
basemaps.landsat.availableYears.map(y => ({
label: y,
value: y
}))
});
Loading

0 comments on commit 62a386f

Please sign in to comment.