Skip to content

Commit

Permalink
[Maps] add View Control displaying coordinates at mouse position (#28023
Browse files Browse the repository at this point in the history
) (#28104)

* put mouse position in store

* widget overview component

* Fixing layout of overlay (#28)

* move layer_control and layer_toc under widget_overview folder

* clear mouse coordinates when mouse leaves map

* change how settig map view works to avoid state timing bug

* debounce moveend event
  • Loading branch information
nreese authored Jan 4, 2019
1 parent 32d1f01 commit d8c78de
Show file tree
Hide file tree
Showing 21 changed files with 331 additions and 105 deletions.
40 changes: 31 additions & 9 deletions x-pack/plugins/gis/public/_main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,49 @@ map-listing, .gisListingPage {
flex-grow: 1;
}

.LayerControl {
/**
* 1. The overlay captures mouse events even if it's empty space. To counter-act this,
* we remove all pointer events from the overlay then add them back on the
* individual widgets.
*/

.gisWidgetOverlay {
position: absolute;
z-index: 100;
z-index: $euiZLevel1;
min-width: 17rem;
max-width: 24rem;
top: $euiSizeM;
right: $euiSizeM;
max-width: 24rem;
padding-bottom: 8px;
border-color: transparent;
bottom: $euiSizeM;
pointer-events: none; /* 1 */
}

.gisWidgetControl {
max-height: 100%;
overflow: hidden;
padding-bottom: $euiSizeS; // ensures the scrollbar doesn't appear unnecessarily because of flex group negative margins
border-color: transparent !important;
flex-direction: column;
display: flex;
pointer-events: all; /* 1 */

&.euiPanel--shadow {
@include euiBottomShadowLarge;
}

.LayerControl--header {
padding: 16px 16px 8px;
.gisWidgetControl__header {
padding: $euiSizeS $euiSize;
flex-shrink: 0;
}
}

.gisWidgetControl__tocHolder {
@include euiScrollBar;
overflow-y: auto;
}

.layerEntry {
padding: 8px 16px;
padding: $euiSizeS $euiSize;
position: relative;
}

Expand Down Expand Up @@ -255,4 +277,4 @@ map-listing, .gisListingPage {
.euiComboBox__inputWrap {
display: flex;
}
}
}
29 changes: 29 additions & 0 deletions x-pack/plugins/gis/public/actions/store_actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ export const TOUCH_LAYER = 'TOUCH_LAYER';
export const UPDATE_LAYER_ALPHA_VALUE = 'UPDATE_LAYER_ALPHA_VALUE';
export const UPDATE_SOURCE_PROP = 'UPDATE_SOURCE_PROP';
export const SET_REFRESH_CONFIG = 'SET_REFRESH_CONFIG';
export const SET_MOUSE_COORDINATES = 'SET_MOUSE_COORDINATES';
export const CLEAR_MOUSE_COORDINATES = 'CLEAR_MOUSE_COORDINATES';
export const SET_GOTO = 'SET_GOTO';
export const CLEAR_GOTO = 'CLEAR_GOTO';

const GIS_API_RELATIVE = `../${GIS_API_PATH}`;

Expand Down Expand Up @@ -226,6 +230,31 @@ export function mapExtentChanged(newMapConstants) {
};
}

export function setMouseCoordinates({ lat, lon }) {
return {
type: SET_MOUSE_COORDINATES,
lat,
lon,
};
}

export function clearMouseCoordinates() {
return { type: CLEAR_MOUSE_COORDINATES };
}

export function setGoto({ lat, lon, zoom }) {
return {
type: SET_GOTO,
lat,
lon,
zoom,
};
}

export function clearGoto() {
return { type: CLEAR_GOTO };
}

export function startDataLoad(layerId, dataId, requestToken, meta = {}) {
return ({
meta,
Expand Down
7 changes: 4 additions & 3 deletions x-pack/plugins/gis/public/angular/map_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
setSelectedLayer,
setTimeFilters,
setRefreshConfig,
mapExtentChanged,
setGoto,
replaceLayerList,
} from '../actions/store_actions';
import { getIsDarkTheme, updateFlyout, FLYOUT_STATE } from '../store/ui';
Expand Down Expand Up @@ -56,9 +56,10 @@ app.controller('GisMapController', ($scope, $route, config, kbnUrl) => {
const mapState = JSON.parse(savedMap.mapStateJSON);
const timeFilters = mapState.timeFilters ? mapState.timeFilters : timefilter.getTime();
store.dispatch(setTimeFilters(timeFilters));
store.dispatch(mapExtentChanged({
store.dispatch(setGoto({
lat: mapState.center.lat,
lon: mapState.center.lon,
zoom: mapState.zoom,
center: mapState.center,
}));
if (mapState.refreshConfig) {
store.dispatch(setRefreshConfig(mapState.refreshConfig));
Expand Down
4 changes: 2 additions & 2 deletions x-pack/plugins/gis/public/components/gis_map/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import React, { Component } from 'react';
import { MBMapContainer } from '../map/mb';
import { LayerControl } from '../layer_control/index';
import { WidgetOverlay } from '../widget_overlay/index';
import { LayerPanel } from '../layer_panel/index';
import { AddLayerPanel } from '../layer_addpanel/index';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
Expand Down Expand Up @@ -78,7 +78,7 @@ export class GisMap extends Component {
<EuiFlexGroup gutterSize="none" responsive={false}>
<EuiFlexItem className="gisMapWrapper">
<MBMapContainer/>
<LayerControl/>
<WidgetOverlay/>
</EuiFlexItem>

<EuiFlexItem className={`gisLayerPanel ${currentPanelClassName}`} grow={false}>
Expand Down
23 changes: 20 additions & 3 deletions x-pack/plugins/gis/public/components/map/mb/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,21 @@

import { connect } from 'react-redux';
import { MBMapContainer } from './view';
import { mapExtentChanged, mapReady, mapDestroyed } from '../../../actions/store_actions';
import { getLayerList, getMapState, getMapReady } from "../../../selectors/map_selectors";
import {
mapExtentChanged,
mapReady,
mapDestroyed,
setMouseCoordinates,
clearMouseCoordinates,
clearGoto,
} from '../../../actions/store_actions';
import { getLayerList, getMapReady, getGoto } from "../../../selectors/map_selectors";

function mapStateToProps(state = {}) {
return {
isMapReady: getMapReady(state),
mapState: getMapState(state),
layerList: getLayerList(state),
goto: getGoto(state),
};
}

Expand All @@ -23,11 +30,21 @@ function mapDispatchToProps(dispatch) {
dispatch(mapExtentChanged(e));
},
onMapReady: (e) => {
dispatch(clearGoto());
dispatch(mapExtentChanged(e));
dispatch(mapReady());
},
onMapDestroyed: () => {
dispatch(mapDestroyed());
},
setMouseCoordinates: ({ lat, lon }) => {
dispatch(setMouseCoordinates({ lat, lon }));
},
clearMouseCoordinates: () => {
dispatch(clearMouseCoordinates());
},
clearGoto: () => {
dispatch(clearGoto());
}
};
}
Expand Down
12 changes: 5 additions & 7 deletions x-pack/plugins/gis/public/components/map/mb/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import _ from 'lodash';
import mapboxgl from 'mapbox-gl';

export async function createMbMapInstance(node, initialZoom, initialCenter) {
export async function createMbMapInstance(node, initialView) {
return new Promise((resolve) => {
const options = {
container: node,
Expand All @@ -17,13 +17,11 @@ export async function createMbMapInstance(node, initialZoom, initialCenter) {
layers: [],
},
};
if (initialZoom) {
options.zoom = initialZoom;
}
if (initialCenter) {
if (initialView) {
options.zoom = initialView.zoom;
options.center = {
lng: initialCenter.lon,
lat: initialCenter.lat
lng: initialView.lon,
lat: initialView.lat
};
}
const mbMap = new mapboxgl.Map(options);
Expand Down
50 changes: 29 additions & 21 deletions x-pack/plugins/gis/public/components/map/mb/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,7 @@ export class MBMapContainer extends React.Component {
}

async _initializeMap() {
const initialZoom = this.props.mapState.zoom;
const initialCenter = this.props.mapState.center;
this._mbMap = await createMbMapInstance(this.refs.mapContainer, initialZoom, initialCenter);
window._mbMap = this._mbMap;
this._mbMap = await createMbMapInstance(this.refs.mapContainer, this.props.goto);

// Override mapboxgl.Map "on" and "removeLayer" methods so we can track layer listeners
// Tracked layer listerners are used to clean up event handlers
Expand All @@ -90,9 +87,27 @@ export class MBMapContainer extends React.Component {
};

this.assignSizeWatch();
this._mbMap.on('moveend', () => {

// moveend callback is debounced to avoid updating map extent state while map extent is still changing
// moveend is fired while the map extent is still changing in the following scenarios
// 1) During opening/closing of layer details panel, the EUI animation results in 8 moveend events
// 2) Setting map zoom and center from goto is done in 2 API calls, resulting in 2 moveend events
this._mbMap.on('moveend', _.debounce(() => {
this.props.extentChanged(this._getMapState());
}, 100));

const throttledSetMouseCoordinates = _.throttle(e => {
this.props.setMouseCoordinates({
lat: _.round(e.lngLat.lat, DECIMAL_DEGREES_PRECISION),
lon: _.round(e.lngLat.lng, DECIMAL_DEGREES_PRECISION)
});
}, 100);
this._mbMap.on('mousemove', throttledSetMouseCoordinates);
this._mbMap.on('mouseout', () => {
throttledSetMouseCoordinates.cancel(); // cancel any delayed setMouseCoordinates invocations
this.props.clearMouseCoordinates();
});

this.props.onMapReady(this._getMapState());
}

Expand Down Expand Up @@ -145,27 +160,20 @@ export class MBMapContainer extends React.Component {
_syncMbMapWithMapState = () => {
const {
isMapReady,
mapState,
goto,
clearGoto,
} = this.props;

if (!isMapReady) {
if (!isMapReady || !goto) {
return;
}

const zoom = _.round(this._mbMap.getZoom(), ZOOM_PRECISION);
if (typeof mapState.zoom === 'number' && mapState.zoom !== zoom) {
this._mbMap.setZoom(mapState.zoom);
}

const center = this._mbMap.getCenter();
if (mapState.center &&
(mapState.center.lat !== _.round(center.lat, DECIMAL_DEGREES_PRECISION)
|| mapState.center.lon !== _.round(center.lng, DECIMAL_DEGREES_PRECISION))) {
this._mbMap.setCenter({
lng: mapState.center.lon,
lat: mapState.center.lat
});
}
clearGoto();
this._mbMap.setZoom(goto.zoom);
this._mbMap.setCenter({
lng: goto.lon,
lat: goto.lat
});
}

_syncMbMapWithLayerList = () => {
Expand Down
11 changes: 11 additions & 0 deletions x-pack/plugins/gis/public/components/widget_overlay/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { connect } from 'react-redux';
import { WidgetOverlay } from './widget_overlay';

const connectedWidgetOverlay = connect(null, null)(WidgetOverlay);
export { connectedWidgetOverlay as WidgetOverlay };
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { connect } from 'react-redux';
import { LayerControl } from './view';
import {
updateFlyout,
FLYOUT_STATE
} from '../../../store/ui';

function mapDispatchToProps(dispatch) {
return {
showAddLayerWizard: () => {
dispatch(updateFlyout(FLYOUT_STATE.ADD_LAYER_WIZARD));
},
};
}

const connectedLayerControl = connect(null, mapDispatchToProps)(LayerControl);
export { connectedLayerControl as LayerControl };
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

import { connect } from 'react-redux';
import { LayerTOC } from './view';
import { updateLayerOrder } from "../../actions/store_actions";
import { getLayerList } from "../../selectors/map_selectors";
import { updateLayerOrder } from "../../../../actions/store_actions";
import { getLayerList } from "../../../../selectors/map_selectors";

const mapDispatchToProps = {
updateLayerOrder: newOrder => updateLayerOrder(newOrder)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
import _ from 'lodash';
import { connect } from 'react-redux';
import { TOCEntry } from './toc_entry';
import { updateFlyout, FLYOUT_STATE } from '../../../store/ui';
import { setSelectedLayer, toggleLayerVisible } from '../../../actions/store_actions';
import { updateFlyout, FLYOUT_STATE } from '../../../../../store/ui';
import { setSelectedLayer, toggleLayerVisible } from '../../../../../actions/store_actions';

function mapStateToProps(state = {}) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
EuiIconTip,
EuiSpacer
} from '@elastic/eui';
import { VisibilityToggle } from '../../../shared/components/visibility_toggle';
import { VisibilityToggle } from '../../../../../shared/components/visibility_toggle';

export class TOCEntry extends React.Component {

Expand Down
Loading

0 comments on commit d8c78de

Please sign in to comment.