-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feat] Create polygons - Geo-Operations 1 #595
Conversation
ae03840
to
78f3e1a
Compare
9a5d7fd
to
1376d7a
Compare
Depends on uber/nebula.gl#254 |
@heshan0131 this is still WIP but it's touching a lof of files and I would love to have an early feedback |
@@ -1,6 +1,6 @@ | |||
# Polygon |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it by accident that all Polygon
is renamed to DrawPolygon
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, maybe a replace all side effect. I will make sure I revert the right ones
d1289bd
to
11fe20e
Compare
@heshan0131 Reverted all changes and fixed lint errors and warnings |
src/components/map-container.js
Outdated
@@ -173,86 +259,8 @@ export default function MapContainerFactory(MapPopover, MapControl) { | |||
}; | |||
|
|||
/* component render functions */ | |||
/* eslint-disable complexity */ | |||
_renderMapPopover() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i converted this method into a react component to take advantage of react render optimization
src/components/map/map-control.js
Outdated
|
||
const MapLegendPanel = ({items, isActive, scale, toggleMenuPanel, isExport}) => | ||
const MapLegendPanel = React.memo(({items, isActive, scale, onToggleMenuPanel, isExport}) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
React.memo aka PureComponent for stateless react
@@ -39,8 +39,7 @@ const propTypes = { | |||
onClose: PropTypes.func.isRequired, | |||
onChangeExportSelectedDataset: PropTypes.func.isRequired, | |||
onChangeExportDataType: PropTypes.func.isRequired, | |||
onChangeExportFiltered: PropTypes.func.isRequired, | |||
onChangeExportConfig: PropTypes.func.isRequired |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not used
11fe20e
to
832376a
Compare
510d062
to
9704108
Compare
@heshan0131 updated pull request, let me know what you think |
font-size: 12px; | ||
line-height: 14px; | ||
padding: 8px; | ||
height: 32px; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
switchHeight
and switchWidth
is in base.js, this should be added too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
`; | ||
|
||
const ToolbarItem = React.memo(({active, className, icon, label, onClick}) => ( | ||
<StyledDiv active={active} className="save-export-dropdown__item" onClick={(e) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
might want to change the class name here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
</StyledDiv> | ||
)); | ||
|
||
ToolbarItem.displayName = 'ToolbarItem'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indeed an issue with React facebook/react#14319.
I tried to use react-tools on my browser and this is what i have:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/components/map-container.js
Outdated
mapStyle={mapStyle.bottomMapStyle} | ||
getCursor={this.props.hoverInfo ? () => 'pointer' : undefined} | ||
transitionDuration={TRANSITION_DURATION} | ||
onMouseMove={this.props.visStateActions.onMouseMove} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onMouseMove
is removed. tooltop can't render without mouse pos
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think i accidentally removed it when i rebased. It's back now
src/components/map-container.js
Outdated
layerHoverProp={layerHoverProp} | ||
coordinate={interactionConfig.coordinate.enabled && ((pinned || {}).coordinate || coordinate)} | ||
freezed={Boolean(clicked || pinned)} | ||
onClose={this._onCloseMapPopover} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
onClose needs to be passed in from prop, this
is accessible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/components/map-container.js
Outdated
// project lnglat to screen so that tooltip follows the object on zoom | ||
const viewport = new WebMercatorViewport(mapState); | ||
const lngLat = clicked ? clicked.lngLat : pinned.coordinate; | ||
position = this._getHoverXY(viewport, lngLat); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this
is not accessible anymore
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/components/map-container.js
Outdated
]; | ||
|
||
export default function MapContainerFactory(MapPopover, MapControl) { | ||
/* eslint-disable complexity */ | ||
const MapTooltip = React.memo(({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of defining it inside MapContainer, it makes more sense to create a factory for MapTooltip
, pass it to MapContainer
as dep, then pass MapPopover
as its dep? so
MapContainer -> MapTooltip -> MapPopover
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/components/kepler-gl.js
Outdated
@@ -244,6 +244,8 @@ function KeplerGlFactory( | |||
mapboxApiAccessToken, | |||
mapboxApiUrl, | |||
mapState, | |||
uiState, | |||
visState, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
items from visState
is already picekd out and passed in mapFieds. If you need editor
from it, just pass editor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
</StyledMapContainer> | ||
); | ||
} | ||
} | ||
|
||
MapContainer.displayName = 'MapContainer'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you don't need displayName
, you automatically get it by defining it as a class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eslint was complaining about all missing display names. Either we add displayName to all classes or disable the eslint.
src/reducers/combined-updaters.js
Outdated
|
||
export const setFeaturesUpdater = (state, payload) => ({ | ||
...state, | ||
visState: visStateFeaturesUpdater(state.visState, payload), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't need a composed updater if an action is handled in each subreducer independently. You can just add [SET_FEATURE]
action handlers to visState
and uiState
reducer
visState = {
....
[ActionTypes.SET_FEATURE]: visStateFeaturesUpdater
}
uiState = {
....
[ActionTypes.SET_FEATURE]: uiStateFeaturesUpdater
}
The time when we need to use combined updaters is when 1 action needs to be handled in multiple reducer is a certain sequence. e.g. in updateVisData
we use layers created by visState
to update viewport in mapState1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i initially started with independent actions and I was trying to see whether there was a performance improvement by using a composed action. But at the end i don't think it made any different
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are still creating setFeaturesComposed
and deleteFeatureComposed
. this 2 actions don't need to be composed here. They can be handled independently inside each (visState, uiState) reducer...
You can delete all new lines inside this file, and it will work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea behind these actions is to be used the way we use addDataToMapComposed, directly accessing kepler.gl store rather than using actions
An application can call these composed updaters when it is directly kepler.gl and want to modify the features. I am not using them at all and since i changed the logic in the second PR i can get rid of them
i am not even using those actions anywhere, i removed them
337365e
to
0864853
Compare
@heshan0131 update with your feedback. Waiting on uber/nebula.gl#260 as well for better interaction handler |
The mouse cursor should be |
Polygons are turned solid when selected |
@heshan0131 Updated cursor pointers |
@@ -51,15 +51,18 @@ export default class Base extends Component { | |||
width: null, | |||
viewBox: '0 0 64 64', | |||
predefinedClassName: '', | |||
className: '' | |||
className: '', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setting fill: 'currentColor'
in defaultProps
means if user passes in a style object, it will override default style (instead of merging with it), not ideal. use style.fill = 'currentColor';
in render simply adds it to user defined value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with style.fill = 'currentColor' is that i cannot override the default value;
I need a way to override the fill property.
we can do something like
style = {}
... } = this.props;
style.fill = style.fill || 'currentColor'
or I can create a helper to merge defaults values when no properties exist in the original object
|
||
static defaultProps = { | ||
height: '16px', | ||
predefinedClassName: 'data-ex-icons-polygon', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'data-ex-icons-rectangle'
.mapboxgl-map .mapboxgl-missing-css { | ||
display: none; | ||
.mapboxgl-map { | ||
z-index: -1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why z-index: -1;
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is due to an issue with deckgl where deckgl capture mouse click events despite of other existing layers and their z-index value. So I have to make sure deckgl and its container mapbox-gl has -1 when in editor mode in order to be able to use the mouse to draw polygons.
When in regular mode we have the following:
mapboxgl: -1
deckgl: 0
editor: -1
In editor mode:
mapboxgl: -1
deckgl: -1
editor: 0
src/components/common/toolbar.js
Outdated
`; | ||
|
||
const Toolbar = React.memo(({children, className, show, direction = 'row'}) => ( | ||
<StyledPanelDropdown className={`${className || ''} save-export-dropdown`} show={show} direction={direction}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here about using classnames
src/components/map-container.js
Outdated
onSelect={uiStateActions.setSelectedFeature} | ||
onUpdate={visStateActions.setFeatures} | ||
style={{zIndex: isEdit ? 0 : -1}} | ||
onToggleFeatureLayer={visStateActions.toggleFeatureLayer} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh i guess it's in geo-operation 2 pr?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's in geo-operation 2 branch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed it from this PR
src/components/map-container.js
Outdated
onDeleteFeature={uiStateActions.deleteFeature} | ||
onSelect={uiStateActions.setSelectedFeature} | ||
onUpdate={visStateActions.setFeatures} | ||
style={{zIndex: isEdit ? 0 : -1}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changing the z-index, in this case, will not fix the issue because all layers are rendered onto a canvas and the editor is a regular html element.
In order to have the effect you describe we need to absolute position the editor container but in that case deck.gl will not receive mouse over and the tooltip will not show up
<div style={{position: 'relative'}}> | ||
{isActive ? ( | ||
<StyledToolBar show={isActive} direction="column"> | ||
<ToolbarItem |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that clicking on polygon automatically selects it. and click outside polygon deselects it, Do we still need this to manually enter selection mode?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need this for two reasons:
- enable drawing on the map
- the selection of a polygon only works when we are in editor mode. when we turn editor mode off we are not able to select polygons by clicking on it
<ToolbarItem | ||
onClick={() => onSetEditorMode(EDITOR_MODES.DRAW_RECTANGLE)} | ||
label="rectangle" | ||
icon={(<Rectangle height="22px"/>)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A better way is to pass in Rectangle
and render it in <ToolbarItem><props.icon height="22px"></ToolbarItem>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how would we handle all toolbarItems, each has a different icon.
Should we pass a default prop items with a list of button objects? something like this:
{
onClick: () => onSetEditorMode(EDITOR_MODES.DRAW_POLYGON),
label:'polygon',
icon: Rectangle,
activeCondition: EDITOR_MODES.DRAW_RECTANGLE
}
src/components/map/map-tooltip.js
Outdated
@@ -0,0 +1,112 @@ | |||
// Copyright (c) 2019 Uber Technologies, Inc. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this used anywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot to add this to MapContainer. this will replace _renderMapPopover method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will remove it from this diff
346d614
to
22bb2ba
Compare
@heshan0131 I found a more simple and elegant solution for z-index and mouse events. The Draw component will be placed using absolute position over deck.gl (canvas). Modes
|
d6972f5
to
f755b1e
Compare
f755b1e
to
efec164
Compare
efec164
to
9d9a1b5
Compare
7edc2c6
to
cbbbab9
Compare
4071267
to
5401267
Compare
Signed-off-by: Giuseppe Macri <[email protected]>
5401267
to
324f51a
Compare
* Apply Geo-Polygon filters Co-authored-by: Shan He <[email protected]> Signed-off-by: Giuseppe Macri <[email protected]>
15e77fe
to
ff5596e
Compare
This pull requests introduce a new Edit mode which will allow users to draw custom polygons on a map to be used as layer filter (to be implemented later).
We support the ability to create/move/delete rectangle or customd shape polygons,
Context menu and delete action
Selecting a feature and clicking on delete button will remove the feature.
Layers
The current implementation supports adding new points to existing features
Implementation details:
In order to implement the dropdown menu for editing mode i re-used the toolbar implementation currently used for sharing features.
I extracted the react component from sharing and moved into it's own file to be re-used in multiple both sharing and editing mode.
I created a new editor folder containing all react components for the editor mode, including a new context menu action panel.
The Editor component which contains react-map-gl-draw is located in the same MapContainer as deck.gl layer to facilitate logic to switch from editor to read mode
** There are still few edge cases to handle but the feature is pretty much integrated.