Skip to content

Commit

Permalink
Fixes #2815: elevation support in MousePosition, through elevation la…
Browse files Browse the repository at this point in the history
…yers (#2816)
  • Loading branch information
mbarto authored and offtherailz committed Apr 17, 2018
1 parent 4b1e103 commit a923763
Show file tree
Hide file tree
Showing 37 changed files with 2,063 additions and 91 deletions.
60 changes: 53 additions & 7 deletions docs/developer-guide/maps-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ maxExtent: {number[]} max bbox of the map expressed [minx, miny, maxx, maxy]
layers: {object[]} list of layers to be loaded on the map

i.e.
> ``{ "projection": "EPSG:900913",
``` javascript
{ "projection": "EPSG:900913",
"units": "m",
"center": {"x": 1000000.000000, "y": 5528000.000000, "crs": "EPSG:900913"},
"zoom": 15,
Expand All @@ -17,18 +18,22 @@ i.e.
20037508.34, 20037508.34
],
"layers": [{...},{...}]
}``
}
```
# Layers option

i.e.
> ``{
``` javascript
{
"url": "http..."
"format": "image/png8"
"title": "Open Street Map",
"name": "mapnik",
"group": "background",
"visibility": false
}``
"visibility": false,
"hidden": true
}
```

## Layer types

Expand All @@ -41,6 +46,45 @@ i.e.

### WMS

#### Elevation layer
WMS layers can be configured to be used as a source for elevation related functions.
This requires:
* a GeoServer WMS service with the DDS/BIL plugin
* A WMS layer configured with BIL 16 bit output in big endian mode and -9999 nodata value
* a static layer in the Map plugin configuration (use the additionalLayers configuration option):

**in localConfig.json**
``` javascript
{
"name": "Map",
"cfg": {
"additionalLayers": [{
"url": "http..."
"format": "application/bil16",
...
"name": "elevation",
"visibility": true,
"useForElevation": true
}]
}
}
```

The layer will be used for:
* showing elevation in the MousePosition plugin (requires showElevation: true in the plugin configuration)
* as a TerrainProvider if the maptype is Cesium

**in localConfig.json**
``` javascript
{
"name": "MousePosition",
"cfg": {
"showElevation": true,
...
}
}
```

### Bing

### Google
Expand All @@ -54,14 +98,16 @@ It's enough to add provider property and 'tileprovider' as type property to the
List of available layer [here](https://github.com/geosolutions-it/MapStore2/blob/master/web/client/utils/ConfigProvider.js)

i.e.
> ``{
``` javascript
{
"type": "tileprovider",
"title": "Title",
"provider": "Stamen.Toner",
"name": "Name",
"group": "GroupName",
"visibility": false
}``
}
```

Options passed in configuration object, if already configured by TileProvider, will be overridden.

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
"leaflet.locatecontrol": "0.62.0",
"leaflet.nontiledlayer": "1.0.7",
"lodash": "4.16.6",
"lru-cache": "4.1.2",
"moment": "2.21.0",
"node-geo-distance": "1.2.0",
"object-assign": "4.1.1",
Expand Down
90 changes: 48 additions & 42 deletions web/client/components/TOC/DefaultLayer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class DefaultLayer extends React.Component {
filterText: PropTypes.string,
onUpdateNode: PropTypes.func,
titleTooltip: PropTypes.bool,
filter: PropTypes.func,
showFullTitleOnExpand: PropTypes.bool
};

Expand All @@ -56,41 +57,47 @@ class DefaultLayer extends React.Component {
selectedNodes: [],
filterText: '',
onUpdateNode: () => {},
filter: () => true,
titleTooltip: false,
showFullTitleOnExpand: false
};

getTitle = (layer) => {
const translation = isObject(layer.title) ? layer.title[this.props.currentLocale] || layer.title.default : layer.title;
return translation || layer.name;
};

renderCollapsible = () => {
const layerOpacity = this.props.node.opacity !== undefined ? Math.round(this.props.node.opacity * 100) : 100;
return (
<div key="legend" position="collapsible" className="collapsible-toc">
<Grid fluid>
{this.props.showFullTitleOnExpand ? <Row><Col xs={12} className="toc-full-title">{this.getTitle(this.props.node)}</Col></Row> : null}
{this.props.activateOpacityTool ?
<Row>
<Row>

<Col xs={12} className="mapstore-slider with-tooltip">
<Slider start={[layerOpacity]}
disabled={!this.props.node.visibility}
range={{min: 0, max: 100}}
tooltips
format={{
from: value => Math.round(value),
to: value => Math.round(value) + ' %'
}}
onChange={(opacity) => {
if (isArray(opacity) && opacity[0]) {
this.props.onUpdateNode(this.props.node.id, 'layers', {opacity: parseFloat(opacity[0].replace(' %', '')) / 100});
}
}}/>
</Col>
</Row> : null}
<Col xs={12} className="mapstore-slider with-tooltip">
<Slider start={[layerOpacity]}
disabled={!this.props.node.visibility}
range={{ min: 0, max: 100 }}
tooltips
format={{
from: value => Math.round(value),
to: value => Math.round(value) + ' %'
}}
onChange={(opacity) => {
if (isArray(opacity) && opacity[0]) {
this.props.onUpdateNode(this.props.node.id, 'layers', { opacity: parseFloat(opacity[0].replace(' %', '')) / 100 });
}
}} />
</Col>
</Row> : null}
{this.props.activateLegendTool ?
<Row>
<Col xs={12}>
<WMSLegend node={this.props.node} currentZoomLvl={this.props.currentZoomLvl} scales={this.props.scales} {...this.props.legendOptions}/>
</Col>
</Row> : null}
<Row>
<Col xs={12}>
<WMSLegend node={this.props.node} currentZoomLvl={this.props.currentZoomLvl} scales={this.props.scales} {...this.props.legendOptions} />
</Col>
</Row> : null}
</Grid>
</div>);
};
Expand All @@ -100,27 +107,27 @@ class DefaultLayer extends React.Component {
(<LayersTool key="loadingerror"
glyph="exclamation-mark text-danger"
tooltip="toc.loadingerror"
className="toc-error"/>)
className="toc-error" />)
:
(<VisibilityCheck key="visibilitycheck"
tooltip={this.props.node.loadingError === 'Warning' ? 'toc.toggleLayerVisibilityWarning' : 'toc.toggleLayerVisibility'}
node={this.props.node}
checkType={this.props.visibilityCheckType}
propertiesChangeHandler={this.props.propertiesChangeHandler}/>);
propertiesChangeHandler={this.props.propertiesChangeHandler} />);
}

renderToolsLegend = (isEmpty) => {
return this.props.node.loadingError === 'Error' || isEmpty ?
null
:
(<LayersTool
node={this.props.node}
tooltip="toc.displayLegendAndTools"
key="toollegend"
className="toc-legend"
ref="target"
glyph="chevron-left"
onClick={(node) => this.props.onToggle(node.id, node.expanded)}/>);
null
:
(<LayersTool
node={this.props.node}
tooltip="toc.displayLegendAndTools"
key="toollegend"
className="toc-legend"
ref="target"
glyph="chevron-left"
onClick={(node) => this.props.onToggle(node.id, node.expanded)} />);
}

renderNode = (grab, hide, selected, error, warning, other) => {
Expand All @@ -130,7 +137,7 @@ class DefaultLayer extends React.Component {
<div className="toc-default-layer-head">
{grab}
{this.renderVisibility()}
<Title tooltip={this.props.titleTooltip} filterText={this.props.filterText} node={this.props.node} currentLocale={this.props.currentLocale} onClick={this.props.onSelect} onContextMenu={this.props.onContextMenu}/>
<Title tooltip={this.props.titleTooltip} filterText={this.props.filterText} node={this.props.node} currentLocale={this.props.currentLocale} onClick={this.props.onSelect} onContextMenu={this.props.onContextMenu} />
{this.props.node.loading ? <div className="toc-inline-loader"></div> : this.renderToolsLegend(isEmpty)}
</div>
{isEmpty ? null : this.renderCollapsible()}
Expand All @@ -147,18 +154,17 @@ class DefaultLayer extends React.Component {
const warning = this.props.node.loadingError === 'Warning' ? ' layer-warning' : '';
const grab = other.isDraggable ? <LayersTool key="grabTool" tooltip="toc.grabLayerIcon" className="toc-grab" ref="target" glyph="menu-hamburger"/> : <span className="toc-layer-tool toc-grab"/>;
const filteredNode = this.filterLayers(this.props.node) ? this.renderNode(grab, hide, selected, error, warning, other) : null;

return !this.props.filterText ? this.renderNode(grab, hide, selected, error, warning, other) : filteredNode;
}
getTitle = (layer) => {
const translation = isObject(layer.title) ? layer.title[this.props.currentLocale] || layer.title.default : layer.title;
return translation || layer.name;
if (this.props.filter(this.props.node)) {
return !this.props.filterText ? this.renderNode(grab, hide, selected, error, warning, other) : filteredNode;
}
return null;
}

filterLayers = (layer) => {
const translation = isObject(layer.title) ? layer.title[this.props.currentLocale] || layer.title.default : layer.title;
const title = translation || layer.name;
return title.toLowerCase().indexOf(this.props.filterText.toLowerCase()) !== -1;
}
};
}

module.exports = DefaultLayer;
12 changes: 8 additions & 4 deletions web/client/components/map/cesium/Layer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,14 @@ class CesiumLayer extends React.Component {
};

addLayerInternal = (newProps) => {
this.provider = this.props.map.imageryLayers.addImageryProvider(this.layer);
this.provider._position = this.props.position;
if (newProps.options.opacity !== undefined) {
this.provider.alpha = newProps.options.opacity;
if (newProps.options.useForElevation) {
this.props.map.terrainProvider = this.layer;
} else {
this.provider = this.props.map.imageryLayers.addImageryProvider(this.layer);
this.provider._position = this.props.position;
if (newProps.options.opacity !== undefined) {
this.provider.alpha = newProps.options.opacity;
}
}
};

Expand Down
2 changes: 2 additions & 0 deletions web/client/components/map/cesium/Map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,11 @@ class CesiumMap extends React.Component {
const cartesian = this.map.camera.pickEllipsoid(movement.endPosition, this.map.scene.globe.ellipsoid);
let cartographic = ClickUtils.getMouseXYZ(this.map, movement) || cartesian && Cesium.Cartographic.fromCartesian(cartesian);
if (cartographic) {
const elevation = Math.round(cartographic.height);
this.props.onMouseMove({
y: cartographic.latitude * 180.0 / Math.PI,
x: cartographic.longitude * 180.0 / Math.PI,
z: elevation,
crs: "EPSG:4326"
});
}
Expand Down
15 changes: 15 additions & 0 deletions web/client/components/map/cesium/__tests__/Map-test-chrome.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,21 @@ describe('CesiumMap', () => {
expect(map.map.imageryLayers.length).toBe(1);
});

it('check layers for elevation', () => {
var options = {
"url": "http://fake",
"name": "mylayer",
"visibility": true,
"useForElevation": true
};
const map = ReactDOM.render(<CesiumMap center={{ y: 43.9, x: 10.3 }} zoom={11}>
<CesiumLayer type="wms" options={options} />
</CesiumMap>, document.getElementById("container"));
expect(map).toExist();
expect(map.map.terrainProvider).toExist();
expect(map.map.terrainProvider.layerName).toBe('mylayer');
});

it('check if the handler for "moveend" event is called', (done) => {
const expectedCalls = 1;
const precision = 1000000000;
Expand Down
22 changes: 21 additions & 1 deletion web/client/components/map/cesium/plugins/WMSLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
*/

const Layers = require('../../../../utils/cesium/Layers');
const Cesium = require('../../../../libs/cesium');
const BILTerrainProvider = require('../../../../utils/cesium/BILTerrainProvider')(Cesium);
const ConfigUtils = require('../../../../utils/ConfigUtils');
const ProxyUtils = require('../../../../utils/ProxyUtils');
const Cesium = require('../../../../libs/cesium');
const assign = require('object-assign');
const {isArray} = require('lodash');
const WMSUtils = require('../../../../utils/cesium/WMSUtils');
Expand Down Expand Up @@ -103,8 +104,27 @@ function wmsToCesiumOptions(options) {
});
}

function wmsToCesiumOptionsBIL(options) {
let proxyUrl = ConfigUtils.getProxyUrl({});
let proxy;
let url = options.url;
if (proxyUrl) {
proxy = ProxyUtils.needProxy(options.url) && proxyUrl;
if (proxy) {
url = proxy + encodeURIComponent(url);
}
}
return assign({
url,
layerName: options.name
});
}

const createLayer = (options) => {
let layer;
if (options.useForElevation) {
return new BILTerrainProvider(wmsToCesiumOptionsBIL(options));
}
if (options.singleTile) {
layer = new Cesium.SingleTileImageryProvider(wmsToCesiumOptionsSingleTile(options));
} else {
Expand Down
7 changes: 6 additions & 1 deletion web/client/components/map/leaflet/Map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ class LeafletMap extends React.Component {
},
latlng: {
lat: event.latlng.lat,
lng: event.latlng.lng
lng: event.latlng.lng,
z: this.elevationLayer && this.elevationLayer.getElevation(event.latlng, event.containerPoint) || undefined
},
modifiers: {
alt: event.originalEvent.altKey,
Expand Down Expand Up @@ -153,6 +154,9 @@ class LeafletMap extends React.Component {
return;
}
event.layer._ms2Added = true;
if (event.layer.getElevation) {
this.elevationLayer = event.layer;
}

// avoid binding if not possible, e.g. for measurement vector layers
if (!event.layer.layerId) {
Expand Down Expand Up @@ -335,6 +339,7 @@ class LeafletMap extends React.Component {
this.props.onMouseMove({
x: pos.lng || 0.0,
y: pos.lat || 0.0,
z: this.elevationLayer && this.elevationLayer.getElevation(pos, event.containerPoint) || undefined,
crs: "EPSG:4326",
pixel: {
x: event.containerPoint.x,
Expand Down
Loading

0 comments on commit a923763

Please sign in to comment.