Skip to content

Commit

Permalink
geosolutions-it#9807 Extruded features in Cesium 3D (geosolutions-it#…
Browse files Browse the repository at this point in the history
  • Loading branch information
allyoucanmap authored Jan 8, 2024
1 parent 0be639d commit 0683c4a
Show file tree
Hide file tree
Showing 37 changed files with 2,919 additions and 1,653 deletions.
12 changes: 11 additions & 1 deletion docs/developer-guide/vector-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@ The `symbolizer` could be of following `kinds`:
| `width` | stroke width of the line | x | x |
| `dasharray` | array that represent the dashed line intervals | x | x |
| `msClampToGround` | this boolean will allow setting the **clampToGround** value for the feature. This would only apply on Cesium maps. | | x |
| `msHeight` | line height in meters, if undefined the line geometry height is used | | x |
| `msHeightReference` | reference to compute the distance of the line geometry, one of **none**, **ground** or **clamp**. This is applied also to the `msExtrudedHeight` property | | x |
| `msExtrudedHeight` | height of the extrusion in meters | | x |
| `msExtrusionRelativeToGeometry` | if true the extrusion height is computed as z distance from the line geometry. When false the extrude height is computed as absolute altitude. When `msExtrusionType` is not `undefined` the extrusion height will be computed always relative to the geometry so this property has not effect | | x |
| `msExtrusionColor` | color of the extruded shape | | x |
| `msExtrusionOpacity` | opacity of the extruded shape | | x |
| `msExtrusionType` | type of extrusion, one of `undefined`, **Circle** or **Square**. If `undefined` a vertical wall plane will be extruded. | | x |

## `Fill` symbolizer properties

Expand All @@ -172,7 +179,10 @@ The `symbolizer` could be of following `kinds`:
| `outlineWidth` | outline width of the polygon | x | x |
| `outlineDasharray` | array that represent the dashed line intervals | x | x |
| `msClassificationType` | allow setting **classificationType** value for the feature. This would only apply on polygon graphics in Cesium maps. | | x |
| `msClampToGround` | this boolean will allow setting the **clampToGround** value for the feature. This would only apply on Cesium maps. | | x |
| `msHeight` | polygon height in meters, if undefined the polygon geometry height is used | | x |
| `msHeightReference` | reference to compute the distance of the polygon geometry, one of **none**, **ground** or **clamp**. This is applied also to the `msExtrudedHeight` property | | x |
| `msExtrudedHeight` | height of the extrusion in meters | | x |
| `msExtrusionRelativeToGeometry` | if true the extrusion height is computed as z distance from the polygon surface. When false the extrude height is computed as absolute altitude | | x |

## `Text` symbolizer properties

Expand Down
12 changes: 2 additions & 10 deletions web/client/api/catalog/WFS.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
testService as commonTestService,
preprocess as commonPreprocess
} from './common';

import { createDefaultStyle } from '../../utils/StyleUtils';
import { get, castArray } from 'lodash';

const capabilitiesCache = {};
Expand Down Expand Up @@ -77,14 +77,6 @@ const searchAndPaginate = (json = {}, startPosition, maxRecords, text) => {
};

const recordToLayer = (record) => {
const DEFAULT_VECTOR_STYLE = {
"weight": 1,
"color": "rgba(0, 0, 255, 1)",
"opacity": 1,
"fillColor": "rgba(0, 0, 255, 0.1)",
"fillOpacity": 0.1,
radius: 10
};
return {
type: record.type || "wfs",
search: {
Expand All @@ -99,7 +91,7 @@ const recordToLayer = (record) => {
description: record.description || "",
bbox: record.boundingBox,
links: getRecordLinks(record),
style: DEFAULT_VECTOR_STYLE,
style: createDefaultStyle({}),
...record.layerOptions
};
};
Expand Down
43 changes: 0 additions & 43 deletions web/client/components/TOC/fragments/MarkIcon.jsx

This file was deleted.

91 changes: 2 additions & 89 deletions web/client/components/TOC/fragments/StyleBasedLegend.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,101 +7,14 @@
*/
import React from 'react';
import PropTypes from 'prop-types';
import MarkIcon from './MarkIcon';
import { Glyphicon } from 'react-bootstrap';
import { parseSymbolizerExpressions } from '../../../utils/styleparser/StyleParserUtils';
import RuleLegendIcon from '../../styleeditor/RuleLegendIcon';

function StyleBasedLegend({ style }) {
const renderIcon = (_symbolizer) => {
const symbolizer = parseSymbolizerExpressions(_symbolizer, { properties: {} });
const {
color,
outlineColor,
width,
outlineWidth,
dasharray,
join,
cap,
opacity,
outlineOpacity,
fillOpacity,
image,
rotate
} = symbolizer;
switch (symbolizer.kind) {
case 'Line':
let displayWidth = width;
if (width === 0) {
displayWidth = 1;
}
if (width > 7) {
displayWidth = 7;
}
return (<svg viewBox="0 0 50 50">
<path d={`M ${displayWidth} ${displayWidth} L ${50 - displayWidth} ${50 - displayWidth}`}
stroke={color}
strokeWidth={displayWidth}
strokeDasharray={dasharray ? "18 18" : null}
strokeLinecap={cap}
strokeLinejoin={join}
strokeOpacity={opacity}
/>
</svg>);
case 'Fill':
return (<svg viewBox="0 0 50 50">
<path d="M 1 1 L 1 49 L 49 49 L 49 1 L 1 1"
fill={color}
opacity={fillOpacity}
stroke={outlineColor}
strokeWidth={outlineWidth}
strokeOpacity={outlineOpacity}
/>
</svg>);
case 'Circle':
return (<svg viewBox="0 0 50 50">
<circle cx="25" cy="25" r="25"
fill={color}
opacity={opacity}
stroke={outlineColor}
strokeWidth={outlineWidth}
strokeOpacity={outlineOpacity}
/>
</svg>);
case 'Mark':
return <MarkIcon symbolizer={symbolizer} />;
case 'Icon':
const svgStyle = { transform: `rotate(${rotate}deg)`, opacity };
return (<svg viewBox="0 0 50 50" style={svgStyle}>
<image href={image} height="50" width="50" />
</svg>);
case 'Model':
return <Glyphicon glyph="model"/>;
case 'Text':
return (<svg viewBox="0 0 16 16">
<text x="8" y="8" text-anchor="middle" alignment-baseline="middle" style={{
fontSize: symbolizer.size < 14 ? symbolizer.size : 14,
fill: symbolizer.color,
fontFamily: symbolizer?.font?.join(', '),
fontStyle: symbolizer.fontStyle,
fontWeight: symbolizer.fontWeight,
...((symbolizer.haloWidth && symbolizer.haloColor) && {
paintOrder: 'stroke',
stroke: symbolizer.haloColor,
strokeWidth: 1,
strokeLinecap: 'round',
strokeLinejoin: 'round'
})
}} >La</text>
</svg>);
default:
return null;
}
};

const renderRules = (rules) => {
return (rules || []).map((rule) => {
return (<div className="wfs-legend-rule" key={rule.ruleId}>
<div className="wfs-legend-icon">{renderIcon(rule.symbolizers[0])}</div>
<RuleLegendIcon rule={rule} />
<span>{rule.name || ''}</span>
</div>);
});
Expand Down
66 changes: 0 additions & 66 deletions web/client/components/TOC/fragments/__tests__/MarkIcon-test.jsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ describe('StyleBasedLegend module component', () => {
expect(ruleElements.length).toBe(1);
const textElement = ruleElements[0].getElementsByTagName('span');
expect(textElement[0].innerHTML).toBe('Some Line');
const iconContainerElement = ruleElements[0].querySelectorAll('.wfs-legend-icon');
const iconContainerElement = ruleElements[0].querySelectorAll('.ms-rule-legend-icon');
expect(iconContainerElement.length).toBe(1);
const expectedSVG = '<svg viewBox="0 0 50 50"><path d="M 7 7 L 43 43" stroke="#8426c9" stroke-width="7" stroke-dasharray="18 18" stroke-linecap="butt" stroke-linejoin="round" stroke-opacity="0.5"></path></svg>';
expect(iconContainerElement[0].innerHTML).toBe(expectedSVG);
Expand Down Expand Up @@ -84,7 +84,7 @@ describe('StyleBasedLegend module component', () => {
expect(ruleElements.length).toBe(1);
const textElement = ruleElements[0].getElementsByTagName('span');
expect(textElement[0].innerHTML).toBe('Some Line');
const iconContainerElement = ruleElements[0].querySelectorAll('.wfs-legend-icon');
const iconContainerElement = ruleElements[0].querySelectorAll('.ms-rule-legend-icon');
expect(iconContainerElement.length).toBe(1);
const expectedSVG = '<svg viewBox="0 0 50 50"><path d="M 1 1 L 49 49" stroke="#8426c9" stroke-width="1" stroke-linecap="butt" stroke-linejoin="round" stroke-opacity="0.5"></path></svg>';
expect(iconContainerElement[0].innerHTML).toBe(expectedSVG);
Expand Down Expand Up @@ -117,7 +117,7 @@ describe('StyleBasedLegend module component', () => {
expect(ruleElements.length).toBe(1);
const textElement = ruleElements[0].getElementsByTagName('span');
expect(textElement[0].innerHTML).toBe('Some Line');
const iconContainerElement = ruleElements[0].querySelectorAll('.wfs-legend-icon');
const iconContainerElement = ruleElements[0].querySelectorAll('.ms-rule-legend-icon');
expect(iconContainerElement.length).toBe(1);
const expectedSVG = '<svg viewBox="0 0 50 50"><path d="M 7 7 L 43 43" stroke="#8426c9" stroke-width="7" stroke-linecap="butt" stroke-linejoin="round" stroke-opacity="0.5"></path></svg>';
expect(iconContainerElement[0].innerHTML).toBe(expectedSVG);
Expand Down Expand Up @@ -152,7 +152,7 @@ describe('StyleBasedLegend module component', () => {
expect(ruleElements.length).toBe(1);
const textElement = ruleElements[0].getElementsByTagName('span');
expect(textElement[0].innerHTML).toBe('Some polygon');
const iconContainerElement = ruleElements[0].querySelectorAll('.wfs-legend-icon');
const iconContainerElement = ruleElements[0].querySelectorAll('.ms-rule-legend-icon');
expect(iconContainerElement.length).toBe(1);
const expectedSVG = '<svg viewBox="0 0 50 50"><path d="M 1 1 L 1 49 L 49 49 L 49 1 L 1 1" fill="#28ee50" opacity="1" stroke="#17ad31" stroke-width="6" stroke-opacity="1"></path></svg>';
expect(iconContainerElement[0].innerHTML).toBe(expectedSVG);
Expand Down Expand Up @@ -190,7 +190,7 @@ describe('StyleBasedLegend module component', () => {
expect(ruleElements.length).toBe(1);
const textElement = ruleElements[0].getElementsByTagName('span');
expect(textElement[0].innerHTML).toBe('Some mark');
const iconContainerElement = ruleElements[0].querySelectorAll('.wfs-legend-icon');
const iconContainerElement = ruleElements[0].querySelectorAll('.ms-rule-legend-icon');
expect(iconContainerElement.length).toBe(1);
const expectedSVG = '<svg viewBox="0 0 50 50" style="transform: rotate(55deg);"></svg>';
expect(iconContainerElement[0].innerHTML).toBe(expectedSVG);
Expand Down Expand Up @@ -223,11 +223,7 @@ describe('StyleBasedLegend module component', () => {
const ruleElements = document.querySelectorAll('.wfs-legend-rule');
expect(ruleElements.length).toBe(1);
const textElement = ruleElements[0].getElementsByTagName('span');
expect(textElement[0].innerHTML).toBe('Some icon');
const iconContainerElement = ruleElements[0].querySelectorAll('.wfs-legend-icon');
expect(iconContainerElement.length).toBe(1);
const expectedSVG = '<svg viewBox="0 0 50 50" style="transform: rotate(0deg); opacity: 1;"><image href="https://url.to.image" height="50" width="50"></image></svg>';
expect(iconContainerElement[0].innerHTML).toBe(expectedSVG);
expect(textElement[0].getAttribute('class')).toBe('glyphicon glyphicon-point');
});

it('Should render multiple items', () => {
Expand Down
13 changes: 9 additions & 4 deletions web/client/components/map/cesium/Map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,10 @@ class CesiumMap extends React.Component {
// for consistency with 2D view we allow to drill pick through the first feature
// and intersect all the features behind
const features = map.scene.drillPick(position).filter((aFeature) => {
const isQueryable = aFeature?.id?._msIsQueryable || aFeature?.primitive?._msIsQueryable;
if (isQueryable) {
return isQueryable();
}
return !(aFeature?.id?.entityCollection?.owner?.queryable === false);
});
if (features) {
Expand All @@ -359,10 +363,11 @@ class CesiumMap extends React.Component {
// it has content but refers to the whole tile model
const getPropertyIds = feature.getPropertyIds();
properties = Object.fromEntries(getPropertyIds.map(key => [key, feature.getProperty(key)]));
} else if (feature?.id instanceof Cesium.Entity && feature.id.id && feature.id.properties) {
const {properties: {propertyNames}, entityCollection: {owner: {name}}} = feature.id;
properties = Object.fromEntries(propertyNames.map(key => [key, feature.id.properties[key].getValue(0)]));
msId = name;
} else if (feature?.id?._msGetFeatureById || feature?.primitive?._msGetFeatureById) {
const getFeatureById = feature?.id?._msGetFeatureById || feature?.primitive?._msGetFeatureById;
const value = getFeatureById(feature.id);
properties = value.feature.properties;
msId = value.msId;
}
if (!properties || !msId) {
return acc;
Expand Down
Loading

0 comments on commit 0683c4a

Please sign in to comment.