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

Update vectorlayer styling in map-cesium #207

Merged
merged 12 commits into from
Dec 8, 2023
Merged
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@

* **@dlr-eoc/map-cesium:**
- New UKIS library for working with [CesiumJS](https://github.com/CesiumGS/cesium) was added.

- A new example has been added to the demo-maps to show how to work with the new cesium library.
- The GeoJsonDatasource in map-cesium was updated and styling of vector layers is now possible.

* **@dlr-eoc/services-layers:**
- The `layer.popup` gets more types and uses `IPopupParams` which will be applied to all popups.
Expand Down
40 changes: 17 additions & 23 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { MapOlService } from '@dlr-eoc/map-ol';
import olMap from 'ol/Map';
import { Cesium3DTileset, CesiumTerrainProvider, Credit, EllipsoidTerrainProvider, I3SDataProvider, createGooglePhotorealistic3DTileset } from '@cesium/engine';
import testData from '@dlr-eoc/shared-assets/geojson/test.json';
import { Feature } from 'ol';
import { Fill, Stroke, Style } from 'ol/style';

@Component({
selector: 'app-route-example-cesium',
Expand Down Expand Up @@ -197,12 +199,40 @@ export class RouteExampleCesiumComponent implements OnInit, OnDestroy {
}),
new VectorLayer({
id: 'geojson_test',
name: 'GeoJSON Vector Layer',
name: 'GeoJSON Vector Layer (default)',
attribution: `© DLR GeoJSON`,
type: 'geojson',
data: testData,
visible: false,
popup: true
}),
new VectorLayer({
id: 'geojson_test2',
name: 'GeoJSON Vector Layer (styled)',
attribution: `© DLR GeoJSON`,
type: 'geojson',
data: testData,
visible: false,
popup: true,
options: {
style: (feature: Feature) => {
let styles = [];

let polygonStyle = new Style({
stroke: new Stroke({
color: '#FF7400',
width: 1
}),
fill: new Fill({
color: '#FF7400' + '99',

}),
});
styles.push(polygonStyle);
return styles;
},
clampToGround: false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can find a better solution here so that the values are not duplicated in the object.

Here is how this object is used in the other mapping libraries

map-ol:

map-maplibre:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found a way to access the style properties in a similar way as in map-maplibre. Therefore, I removed the duplicate entries.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice.

}
})
];

Expand Down
36 changes: 35 additions & 1 deletion projects/map-cesium/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,45 @@ Instead of a new 3D layer service, the existing [@dlr-eoc/services-layers](https
#### Serving 3D content
At the moment there are not many services out there, which serve 3D data. If you have data in the right format ([Cesium 3D Tiles](https://github.com/CesiumGS/3d-tiles), [Cesium quantized-mesh](https://github.com/CesiumGS/quantized-mesh)) available, everything you need is a web server for making the data accessible to the app. To quickly test the 3D capabilities you could use [http-server](https://github.com/http-party/http-server) to host 3D tiles and terrain meshes locally. A useful tool for generating terrain in the quantized-mesh format is the [Cesium Terrain Builder Docker](https://github.com/tum-gis/cesium-terrain-builder-docker).

#### Styling geoJSON vector layer
GeoJSON layer in Cesium access the layer.options style property. To clamp a vector layer to the ground use `clampToGround: true` as an optional property of layer.options. Here is an example:
```
new VectorLayer({
id: 'geojson_test',
name: 'GeoJSON Vector Layer',
attribution: `© Attribution`,
type: 'geojson',
data: testData,
visible: true,
options: {
style: (feature: Feature) => {
let styles = [];

let polygonStyle = new Style({
stroke: new Stroke({
color: '#FF7400',
width: 1
}),
fill: new Fill({
color: '#FF7400' + '99',

}),
});
styles.push(polygonStyle);
return styles;
},
clampToGround: false
}
})
```

#### Notes
- Terrain has to be attributed with a new Cesium Credit object, see the example above.
- GeoJSON layer are supported, but they are always shown above imagery layers, regardless of their ordering index in the layer control. Therefore they should be added as overlays.
- As of 01/2023, WFS is not supported by Cesium yet.
- KmlDataSource does not support opacity change at the moment
- KmlDataSource does not support opacity change at the moment.
- The stroke-width of vector polygons in Cesium does not change on non Apple browsers. There seems to be an issue with the browser support for this feature.
- When a vector layer is clamped to the ground, Cesium does not display the stroke anymore. Cesium's proposed workaround at the moment is to include the stroke as additional polyline vector layer.


===
Expand Down
4 changes: 2 additions & 2 deletions projects/map-cesium/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"dependencies": {
"@dlr-eoc/services-map-state": "12.0.0-alpha.2",
"@dlr-eoc/services-layers": "12.0.0-alpha.2",
"@cesium/engine": "^3.0.2",
"@cesium/widgets": "^3.0.2",
"@cesium/engine": "^6.1.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updating libraries in the major version could be considered a breaking change.

@voinSR and @MichaelLangbein I think we discussed this once. But with a lot of libraries in our repo, the versions could get very high very quickly. We have to decide how to deal with that.

For now, it is ok because this library was only available as an alpha version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I wanted to update the versions before we create a new release of the libraries.

"@cesium/widgets": "^4.3.0",
"tslib": "^2.4.0"
},
"devDependencies": {
Expand Down
10 changes: 7 additions & 3 deletions projects/map-cesium/src/lib/map-cesium.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { filter } from 'rxjs/operators';
import { MapCesiumService } from './map-cesium.service';
import { LayersService, Layer, TFiltertypes, TFiltertypesUncap } from '@dlr-eoc/services-layers';
import { Viewer } from '@cesium/widgets';
import { GeoJsonDataSource } from '@cesium/engine';


export interface ICesiumControls {
Expand Down Expand Up @@ -157,10 +158,7 @@ export class MapCesiumComponent implements OnInit, AfterViewInit, OnDestroy {
const newCenter = this.mapSvc.getCenter();
const extent = this.mapSvc.getCurrentExtent();
const ms = new MapState(zoom, newCenter, { notifier: 'user' }, extent);
//console.log('New Cesium Map State: ');
//console.log(ms);
this.mapStateSvc.setMapState(ms);
//console.log(this.mapStateSvc.getMapState().getValue());
});

//Changing entitiy parameters for the display in he infoBox window
Expand All @@ -170,6 +168,12 @@ export class MapCesiumComponent implements OnInit, AfterViewInit, OnDestroy {
const titleDiv = this.viewer.infoBox.container.getElementsByClassName('cesium-infoBox-title')[0];
titleDiv.innerHTML = 'Layer Attributes';
if (entity) {
if(entity.entityCollection.owner instanceof GeoJsonDataSource){
titleDiv.innerHTML = entity.entityCollection.owner.name;
entity.name = entity.entityCollection.owner.name;
}else{
entity.name = 'Layer Attributes';
}
if (entity.description) {
const description = entity.description.getValue(this.mapSvc.cesiumCurrentTime);
const contentDiv = this.viewer.infoBox.container.getElementsByClassName('cesium-infoBox-content')[0];
Expand Down
57 changes: 40 additions & 17 deletions projects/map-cesium/src/lib/map-cesium.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { Layer, VectorLayer, CustomLayer, RasterLayer, WmtsLayer, WmsLayer, TGeoExtent, TmsLayertype, WmtsLayertype, WmsLayertype, XyzLayertype, IListMatrixSet, TFiltertypesUncap, TFiltertypes } from '@dlr-eoc/services-layers';

import { ICesiumControls } from './map-cesium.component';
import { Cartesian3, Cesium3DTileStyle, Cesium3DTileset, CesiumTerrainProvider, Color, Credit, DataSource, EllipsoidTerrainProvider, GeoJsonDataSource, I3SDataProvider, ImageryLayer, Ion, JulianDate, KmlDataSource, OpenStreetMapImageryProvider, PrimitiveCollection, Rectangle, TileMapServiceImageryProvider, TimeIntervalCollection, UrlTemplateImageryProvider, WebMapServiceImageryProvider, WebMapTileServiceImageryProvider, WebMercatorTilingScheme, } from '@cesium/engine';
import { Cartesian3, Cesium3DTileStyle, Cesium3DTileset, CesiumTerrainProvider, Color, Credit, DataSource, EllipsoidTerrainProvider, GeoJsonDataSource, I3SDataProvider, ImageryLayer, Ion, JulianDate, KmlDataSource, Rectangle, TileMapServiceImageryProvider, TimeIntervalCollection, UrlTemplateImageryProvider, WebMapServiceImageryProvider, WebMapTileServiceImageryProvider, WebMercatorTilingScheme, } from '@cesium/engine';
import { Viewer } from '@cesium/widgets';
import { IMapCenter } from '@dlr-eoc/services-map-state';

Expand Down Expand Up @@ -416,16 +416,14 @@ export class MapCesiumService {

public set3DUkisLayers(layers: Array<Layer>, filtertype: Tgroupfiltertype) {
const lowerType = filtertype.toLowerCase() as Tgroupfiltertype;
const tempLayers: ImageryLayer[] = [];
this.remove3DLayers(lowerType);

layers.forEach((newLayer) => {
const layer = this.create_3D_layer(newLayer as CustomLayer);
this.create_3D_layer(newLayer as CustomLayer);
})
}

private create_2D_layer(newLayer: Layer) {
//console.log('Creating new '+newLayer.type+' layer: '+ newLayer.name);
let newImageryLayer!: ImageryLayer;
switch (newLayer.type) {
case XyzLayertype:
Expand Down Expand Up @@ -501,8 +499,6 @@ export class MapCesiumService {
}

private create_wms_layer(l: WmsLayer): ImageryLayer {
//console.log(l.name);
//console.log(l);
let defaultFormat = 'image/png';
let maxLevel = 20;
if (l.maxZoom) {
Expand Down Expand Up @@ -624,11 +620,42 @@ export class MapCesiumService {

private create_geojson_layer(l: VectorLayer): GeoJsonDataSource {
const newGeoJsonDataSource = new GeoJsonDataSource();
// https://github.com/CesiumGS/cesium/blob/690b4e8850493c9c208b7bd137e9692cbeeca698/packages/engine/Source/Core/Color.js#L2244
const YELLOW = Object.freeze(Color.fromCssColorString("#FFFF00"));
// default UKIS values
let fillColor = Color.fromCssColorString('#FFFFFF99');
let strokeColor = Color.fromCssColorString('#3399CC');
let strokeWidth = 1;
let strokeOpacity = 1;
let fillOpacity = 1;
let clamp = false;

if(l.options && l.options.style){
const styleProperties = l.options.style(l.data)[0];
if(styleProperties){
fillColor = Color.fromCssColorString(styleProperties.fill_.color_) || fillColor;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not optimal to use private properties from OpenLayers here, but we can change that later.

Maybe we can define some common style properties.

l.options.style{
 fillColor?: string;
 strokeColor?: string;
 strokeWidth?: string;
 strokeOpacity?: number;
 fillOpacity?: number;
 circleRadius?: number;
 iconImg?: any;
 ...
}

@voinSR what do you think about it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be ideal.

strokeColor = Color.fromCssColorString(styleProperties.stroke_.color_) || strokeColor;
strokeWidth = styleProperties.stroke_.width_ || strokeWidth;
}
if(l.options['clampToGround']){
clamp = l.options['clampToGround'];
}
}
// as Cesium cannot handle an opacity for the whole datasource, we need to modify the color opacity,
// in case the cesium color already has an opacity value
if(fillColor.alpha != l.opacity){
fillOpacity = l.opacity*fillColor.alpha;
}else{
fillOpacity = l.opacity;
}
if(strokeColor.alpha != l.opacity){
strokeOpacity = l.opacity*strokeColor.alpha;
}else{
strokeOpacity = l.opacity;
}
const dataSourceOptions = {
fill: YELLOW.withAlpha(l.opacity),
stroke: YELLOW.withAlpha(l.opacity)
fill: fillColor.withAlpha(fillOpacity),
stroke: strokeColor.withAlpha(strokeOpacity),
strokeWidth: strokeWidth,
clampToGround: clamp
} as GeoJsonDataSource.LoadOptions;

if (l.attribution) {
Expand All @@ -637,7 +664,7 @@ export class MapCesiumService {

newGeoJsonDataSource.load(l.data, dataSourceOptions);
newGeoJsonDataSource.show = l.visible;
newGeoJsonDataSource.name = l.id;
newGeoJsonDataSource.name = l.name;

this.dataSourceOpacity.set(l.id, l.opacity);
return newGeoJsonDataSource;
Expand Down Expand Up @@ -1007,7 +1034,7 @@ export class MapCesiumService {
if (viewerLayer) {
const cesiumIndex = dataSourceCollection.indexOf(viewerLayer);
const layerIndex = layers.indexOf(layer);
if (cesiumIndex !== layerIndex) {
if (cesiumIndex !== layerIndex && cesiumIndex >= 0) {
const diffIndex = cesiumIndex - layerIndex;
if (diffIndex < 0) {
// Move layer up in collection
Expand All @@ -1031,7 +1058,7 @@ export class MapCesiumService {
if (viewerLayer) {
const cesiumIndex = dataSourceCollection.indexOf(viewerLayer);
const layerIndex = layers.indexOf(layer) + this.getDataSourceLayersSize('baselayers');
if (cesiumIndex !== layerIndex) {
if (cesiumIndex !== layerIndex && cesiumIndex >= 0) {
const diffIndex = cesiumIndex - layerIndex;
if (diffIndex < 0) {
// Move layer up in collection
Expand All @@ -1055,9 +1082,6 @@ export class MapCesiumService {
if (viewerLayer) {
const cesiumIndex = dataSourceCollection.indexOf(viewerLayer);
const layerIndex = layers.indexOf(layer) + this.getDataSourceLayersSize('baselayers') + this.getDataSourceLayersSize('layers');
/* console.log(layer.name);
console.log('CesiumIndex: '+ cesiumIndex);
console.log('LayerIndex: '+ layerIndex); */
if (cesiumIndex !== layerIndex && cesiumIndex >= 0){
const diffIndex = cesiumIndex - layerIndex;
if (diffIndex < 0) {
Expand All @@ -1071,7 +1095,6 @@ export class MapCesiumService {
dataSourceCollection.lower(viewerLayer);
}
}
//console.log('New CesiumIndex: '+ dataSourceCollection.indexOf(viewerLayer));
}
}
}
Expand Down