Skip to content

Commit

Permalink
Fix #9551 Ability to move GLTF 3D models by given x/y (#9779)
Browse files Browse the repository at this point in the history
* Fix #9551 Ability to move GLTF 3D models by given x/y

* add documentation
  • Loading branch information
allyoucanmap authored Dec 1, 2023
1 parent 57ba6d7 commit 3ced0aa
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 17 deletions.
2 changes: 2 additions & 0 deletions docs/developer-guide/vector-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ The `symbolizer` could be of following `kinds`:
| `opacity` | color opacity | | x |
| `msHeightReference` | reference to compute the distance of the point geometry, one of **none**, **ground** or **clamp** | | x |
| `msHeight` | height of the point, the original geometry is applied if undefined | | x |
| `msTranslateX` | move the model on the x axis with a value in meters (west negative value, east positive value) | | x |
| `msTranslateY` | move the model on the y axis with a value in meters (south negative value, north positive value) | | x |
| `msLeaderLineColor` | color of the leading line connecting the point to the terrain | | x |
| `msLeaderLineOpacity` | opacity of the leading line connecting the point to the terrain | | x |
| `msLeaderLineWidth` | width of the leading line connecting the point to the terrain | | x |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,8 @@ describe('VisualStyleEditor', () => {
'styleeditor.color',
'styleeditor.heightReferenceFromGround',
'styleeditor.height',
'styleeditor.msTranslateX',
'styleeditor.msTranslateY',
'styleeditor.leaderLineColor',
'styleeditor.leaderLineWidth'
]);
Expand Down
21 changes: 19 additions & 2 deletions web/client/components/styleeditor/config/blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ const lineGeometryTransformation = () => ({
})
});

const heightPoint3dOptions = ({ isDisabled }) => ({
const heightPoint3dOptions = ({ isDisabled, enableTranslation }) => ({
msHeightReference: property.msHeightReference({
label: "styleeditor.heightReferenceFromGround",
isDisabled
Expand All @@ -148,6 +148,22 @@ const heightPoint3dOptions = ({ isDisabled }) => ({
placeholderId: 'styleeditor.pointHeight',
isDisabled: (value, properties) => isDisabled() || properties?.msHeightReference === 'clamp'
}),
...(enableTranslation && {
msTranslateX: property.number({
key: 'msTranslateX',
label: 'styleeditor.msTranslateX',
uom: 'm',
fallbackValue: 0,
isDisabled
}),
msTranslateY: property.number({
key: 'msTranslateY',
label: 'styleeditor.msTranslateY',
uom: 'm',
fallbackValue: 0,
isDisabled
})
}),
msLeaderLineColor: property.color({
key: 'msLeaderLineColor',
opacityKey: 'msLeaderLineOpacity',
Expand Down Expand Up @@ -556,7 +572,8 @@ const getBlocks = ({
isDisabled: () => !enable3dStyleOptions
}),
...heightPoint3dOptions({
isDisabled: () => !enable3dStyleOptions
isDisabled: () => !enable3dStyleOptions,
enableTranslation: true
}),
...(!shouldHideVectorStyleOptions && pointGeometryTransformation({}))
},
Expand Down
3 changes: 1 addition & 2 deletions web/client/components/styleeditor/config/property.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,7 @@ const property = {
},
getValue: (value) => {
return {
[key]: value,
...(value === 'clamp' && { msHeight: undefined })
[key]: value
};
},
isDisabled
Expand Down
4 changes: 3 additions & 1 deletion web/client/translations/data.de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -2630,7 +2630,9 @@
"offset": "Offset",
"propertyValue": "Eigenschaftswert",
"colorPropertyInfoMessage": "Der Farbeigenschaftswert muss eine hexadezimale Zeichenfolge sein. Beispiel: \"#ffffff\"",
"pointCloudSizeInfo": "Der Punktwolkenradius wird nur angewendet, wenn die Dämpfungsoptionen deaktiviert sind. Die Dämpfungsoption hat Vorrang vor dieser Eigenschaft."
"pointCloudSizeInfo": "Der Punktwolkenradius wird nur angewendet, wenn die Dämpfungsoptionen deaktiviert sind. Die Dämpfungsoption hat Vorrang vor dieser Eigenschaft.",
"msTranslateX": "Versatz x",
"msTranslateY": "Versatz y"
},
"playback": {
"settings": {
Expand Down
4 changes: 3 additions & 1 deletion web/client/translations/data.en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -2602,7 +2602,9 @@
"offset": "Offset",
"propertyValue": "Property value",
"colorPropertyInfoMessage": "The color property value must be an hexadecimal string. Example: \"#ffffff\"",
"pointCloudSizeInfo": "The point cloud radius is applied only when the attenuation options is disabled. The attenuation option takes precedence over this property."
"pointCloudSizeInfo": "The point cloud radius is applied only when the attenuation options is disabled. The attenuation option takes precedence over this property.",
"msTranslateX": "Translate x",
"msTranslateY": "Translate y"
},
"playback": {
"settings": {
Expand Down
4 changes: 3 additions & 1 deletion web/client/translations/data.es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -2592,7 +2592,9 @@
"offset": "Desplazamiento",
"propertyValue": "Valor de la propiedad",
"colorPropertyInfoMessage": "El valor de la propiedad de color debe ser una cadena hexadecimal. Ejemplo: \"#ffffff\"",
"pointCloudSizeInfo": "El radio de la nube de puntos se aplica sólo cuando las opciones de atenuación están deshabilitadas. La opción de atenuación tiene prioridad sobre esta propiedad."
"pointCloudSizeInfo": "El radio de la nube de puntos se aplica sólo cuando las opciones de atenuación están deshabilitadas. La opción de atenuación tiene prioridad sobre esta propiedad.",
"msTranslateX": "Desplazamiento x",
"msTranslateY": "Desplazamiento y"
},
"playback": {
"settings": {
Expand Down
4 changes: 3 additions & 1 deletion web/client/translations/data.fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -2592,7 +2592,9 @@
"offset": "Décalage",
"propertyValue": "Valeur de la propriété",
"colorPropertyInfoMessage": "La valeur de la propriété de couleur doit être une chaîne hexadécimale. Exemple : \"#ffffff\"",
"pointCloudSizeInfo": "Le rayon du nuage de points est appliqué uniquement lorsque les options d'atténuation sont désactivées. L'option d'atténuation est prioritaire sur cette propriété."
"pointCloudSizeInfo": "Le rayon du nuage de points est appliqué uniquement lorsque les options d'atténuation sont désactivées. L'option d'atténuation est prioritaire sur cette propriété.",
"msTranslateX": "Décalage x",
"msTranslateY": "Décalage y"
},
"playback": {
"settings": {
Expand Down
4 changes: 3 additions & 1 deletion web/client/translations/data.it-IT.json
Original file line number Diff line number Diff line change
Expand Up @@ -2593,7 +2593,9 @@
"offset": "Offset",
"propertyValue": "Valore della proprietà",
"colorPropertyInfoMessage": "Il valore della proprietà colore deve essere una stringa esadecimale. Esempio: \"#ffffff\"",
"pointCloudSizeInfo": "Il raggio della nuvola di punti viene applicato solo quando le opzioni di attenuazione sono disabilitate. L'opzione di attenuazione ha la precedenza su questa proprietà."
"pointCloudSizeInfo": "Il raggio della nuvola di punti viene applicato solo quando le opzioni di attenuazione sono disabilitate. L'opzione di attenuazione ha la precedenza su questa proprietà.",
"msTranslateX": "Offset x",
"msTranslateY": "Offset y"
},
"playback": {
"settings": {
Expand Down
62 changes: 54 additions & 8 deletions web/client/utils/styleparser/CesiumStyleParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,19 @@ function createLeaderLineCanvas({
return canvas;
}

const translatePoint = (cartesian, symbolizer) => {
const { msTranslateX, msTranslateY } = symbolizer || {};
const x = getNumberAttributeValue(msTranslateX);
const y = getNumberAttributeValue(msTranslateY);
return (x || y)
? Cesium.Matrix4.multiplyByPoint(
Cesium.Transforms.eastNorthUpToFixedFrame(cartesian),
new Cesium.Cartesian3(x || 0, y || 0, 0),
new Cesium.Cartesian3()
)
: cartesian;
};

function addLeaderLineGraphic({
map,
symbolizer,
Expand All @@ -172,7 +185,9 @@ function addLeaderLineGraphic({
'msLeaderLineWidth',
'msHeight',
'msHeightReference',
'offset'
'offset',
'msTranslateX',
'msTranslateY'
];
const shouldNotUpdateLeaderLine = entity._msSymbolizer
&& !isGlobalOpacityChanged(entity, globalOpacity)
Expand All @@ -195,15 +210,39 @@ function addLeaderLineGraphic({
}

const cartographic = Cesium.Cartographic.fromCartesian(entity.position.getValue(Cesium.JulianDate.now()));
const originalCartographic = Cesium.Cartographic.fromCartesian(entity._msPosition);
const heightReference = symbolizer.msHeightReference;
return (
(
symbolizer?.msHeight !== entity._msSymbolizer?.msHeight
|| symbolizer?.msHeightReference !== entity._msSymbolizer?.msHeightReference
|| symbolizer?.msTranslateX !== entity._msSymbolizer?.msTranslateX
|| symbolizer?.msTranslateY !== entity._msSymbolizer?.msTranslateY
|| !entity.polyline
)
? getLeaderLinePositions({ map, cartographic, heightReference, sampleTerrain })
.then((positions) => new Cesium.PolylineGraphics({ positions }))
? getLeaderLinePositions({
map,
// we create a cartographic that include:
// the original longitude and latitude
// and the modified height
// later we can translate the coordinate connected to the entity
cartographic: new Cesium.Cartographic(
originalCartographic.longitude,
originalCartographic.latitude,
cartographic.height),
heightReference,
sampleTerrain
})
.then((positions) => {
return new Cesium.PolylineGraphics({
positions: [
// original position
positions[0],
// apply translation to the coordinate connected to the entity
translatePoint(positions[1], symbolizer)
]
});
})
: Promise.resolve(entity.polyline)
)
.then((polyline) => {
Expand Down Expand Up @@ -244,13 +283,14 @@ function modifyPointHeight({ entity, symbolizer }) {
const height = getNumberAttributeValue(symbolizer.msHeight);

if (height === null) {
entity.position.setValue(entity._msPosition);
entity.position.setValue(translatePoint(entity._msPosition, symbolizer));
return;
}

const cartographic = Cesium.Cartographic.fromCartesian(entity._msPosition);
cartographic.height = height;
entity.position.setValue(Cesium.Cartographic.toCartesian(cartographic));
const cartesian = Cesium.Cartographic.toCartesian(cartographic);
entity.position.setValue(translatePoint(cartesian, symbolizer));
return;
}

Expand Down Expand Up @@ -742,9 +782,15 @@ function getStyleFuncFromRules({
: getGeometryFunction({ msGeometry: { name: 'centerPoint' }, ...symbolizer });
if (geometryFunction) {
const additionalEntity = entity.entityCollection.add({
position: entity.position
? entity.position.getValue(Cesium.JulianDate.now()).clone()
: new Cesium.Cartesian3(0, 0, 0)
// use the stored position when available
position: entity._msPosition
? entity._msPosition
// if a point geometry we can access de initial value
: entity.position
? entity.position.getValue(Cesium.JulianDate.now()).clone()
// for other computed point we use the geometry function
// so we can apply the origin cartesian
: new Cesium.Cartesian3(0, 0, 0)
});
additionalEntity._msStoredCoordinates = entity._msStoredCoordinates;
additionalEntity._msAdditional = true;
Expand Down
71 changes: 71 additions & 0 deletions web/client/utils/styleparser/__tests__/CesiumStyleParser-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -811,5 +811,76 @@ describe('CesiumStyleParser', () => {
});
});
});
it('should write a style function with model symbolizer with x/y translation', (done) => {

const translateDelta = 100;
const style = {
name: '',
rules: [
{
filter: undefined,
name: '',
symbolizers: [
{
kind: 'Model',
model: '/path/to/file.glb',
scale: 1,
heading: 0,
roll: 0,
pitch: 0,
color: '#ffffff',
opacity: 0.5,
msHeightReference: 'relative',
height: 10,
msTranslateX: translateDelta,
msTranslateY: translateDelta,
msLeaderLineColor: '#ff0000',
msLeaderLineOpacity: 0.5,
msLeaderLineWidth: 2
}
]
}
]
};

const lng = 7;
const lat = 41;

parser.writeStyle(style)
.then((styleFunc) => {
Cesium.GeoJsonDataSource.load({
type: 'Feature',
properties: {},
geometry: {
type: 'Point',
coordinates: [lng, lat]
}
}).then((dataSource) => {
const entities = dataSource?.entities?.values;
return styleFunc({ entities })
.then(() => {
const expectedTranslatedDistance = Math.round(Math.sqrt(2) * translateDelta);
const distancePosition = Math.round(Cesium.Cartesian3.distance(
entities[0]._msPosition,
entities[0].position.getValue(Cesium.JulianDate.now())
));
expect(distancePosition).toBe(expectedTranslatedDistance);
const leaderLinePositions = entities[0].polyline.positions.getValue();
const distanceLeaderLineA = Math.round(Cesium.Cartesian3.distance(
entities[0]._msPosition,
leaderLinePositions[0]
));
expect(distanceLeaderLineA).toBe(0);
const distanceLeaderLineB = Math.round(Cesium.Cartesian3.distance(
entities[0]._msPosition,
leaderLinePositions[1]
));
expect(distanceLeaderLineB).toBe(expectedTranslatedDistance);
done();
})
.catch(done);
});
});
});
});
});

0 comments on commit 3ced0aa

Please sign in to comment.