Skip to content
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

Fixes #2815: elevation support in MousePosition, through elevation layers #2816

Merged
merged 2 commits into from
Apr 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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