Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
8 changes: 7 additions & 1 deletion web/client/components/map/cesium/plugins/WFSLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ const createLayer = (options, map) => {
id: options?.id,
map: map,
opacity: options.opacity,
queryable: options.queryable === undefined || options.queryable
queryable: options.queryable === undefined || options.queryable,
styleRules: options?.style?.body?.rules || []
});
let loader;
let loadingBbox;
Expand Down Expand Up @@ -187,6 +188,11 @@ Layers.registerType('wfs', {
return createLayer(newOptions, map);
}
if (layer?.styledFeatures && !isEqual(newOptions.style, oldOptions.style)) {
// update style rules here
if (!isEqual(newOptions?.style?.body?.rules, oldOptions?.style?.body?.rules)) {
let styleRules = newOptions?.style?.body?.rules || [];
layer.styledFeatures._setStyleRules(styleRules);
}
layerToGeoStylerStyle(newOptions)
.then((style) => {
getStyle(applyDefaultStyleToVectorLayer({
Expand Down
13 changes: 8 additions & 5 deletions web/client/plugins/styleeditor/VectorStyleEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { classifyGeoJSON, availableMethods } from '../../api/GeoJSONClassificati
import { getLayerJSONFeature } from '../../observables/wfs';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { scalesSelector } from '../../selectors/map';
import { currentZoomLevelSelector, scalesSelector } from '../../selectors/map';

const { getColors } = SLDService;

Expand Down Expand Up @@ -88,7 +88,8 @@ function VectorStyleEditor({
'Brush Script MT'
],
onUpdateNode = () => {},
scales = []
scales = [],
zoom = 0
}) {

const request = capabilitiesRequest[layer?.type];
Expand Down Expand Up @@ -258,12 +259,14 @@ function VectorStyleEditor({
supportedSymbolizerMenuOptions: ['Simple', 'Extrusion', 'Classification'],
fonts,
enableFieldExpression: ['vector', 'wfs'].includes(layer.type),
scales
scales,
zoom: Math.round(zoom) // passing this for showing arrow of current scale for ScaleDenominator
}}
/>
);
}
const ConnectedVectorStyleEditor = connect(createSelector([scalesSelector], (scales) => ({
scales: scales.map(scale => Math.round(scale))
const ConnectedVectorStyleEditor = connect(createSelector([scalesSelector, currentZoomLevelSelector], (scales, zoom) => ({
scales: scales.map(scale => Math.round(scale)),
zoom
})))(VectorStyleEditor);
export default ConnectedVectorStyleEditor;
31 changes: 26 additions & 5 deletions web/client/utils/MapUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,25 @@ export function getScale(projection, dpi, resolution) {
return resolution * dpu;
}

/**
* Checks if the camera is looking perpendicular (nadir) to the surface
* @param {Cesium.Camera} camera - The Cesium camera
* @param {Cesium.Cartesian3} position - Position on the globe (Cartesian3)
* @param {Cesium.Ellipsoid} ellipsoid - The ellipsoid (usually scene.globe.ellipsoid)
* @param {number} threshold - Cosine threshold (0.95 = ~18°, 0.99 = ~8°)
* @returns {boolean} True if camera is approximately perpendicular
*/
function isCameraPerpendicularToSurface(camera, position, ellipsoid, threshold = 0.95) {
const surfaceNormal = ellipsoid.geodeticSurfaceNormal(position);
const cameraDirection = camera.direction;

// Dot product: -1 = exactly opposite (straight down), 0 = parallel to surface
const dot = Cesium.Cartesian3.dot(cameraDirection, surfaceNormal);

// Check if dot product is close to -1 (camera looking straight down)
return dot < -threshold;
}

/**
* Calculates the map scale denominator at the center of the Cesium viewer's screen.
*
Expand All @@ -362,7 +381,7 @@ export function getMapScaleForCesium(viewer) {
const scene = viewer.scene;
const camera = scene.camera;
const canvas = scene.canvas;

const ellipsoid = scene.globe.ellipsoid;
// 1. Get two points at the center of the screen, 1 pixel apart horizontally
const centerX = Math.floor(canvas.clientWidth / 2);
const centerY = Math.floor(canvas.clientHeight / 2);
Expand All @@ -377,15 +396,17 @@ export function getMapScaleForCesium(viewer) {
const leftPos = scene.globe.pick(leftRay, scene);
const rightPos = scene.globe.pick(rightRay, scene);

// Check if camera is perpendicular (only if we have a valid position to test against)
const isPerpendicular = Cesium.defined(leftPos) ? isCameraPerpendicularToSurface(camera, leftPos, ellipsoid, 0.95) : false;

if (!Cesium.defined(leftPos) || !Cesium.defined(rightPos)) {
console.warn('Camera is looking at space/sky');
if (!Cesium.defined(leftPos) || !Cesium.defined(rightPos) || isPerpendicular) {
console.warn('Camera is looking at space/sky or is perpendicular');
const cameraPosition = viewer.camera.positionCartographic;
const currentZoom = Math.log2(FALLBACK_EARTH_CIRCUMFERENCE_METERS / (cameraPosition.height)) + 1;
const resolutions = getResolutions();
const resolution = resolutions[Math.round(currentZoom)];
const scaleVal = getScale(cesiumDefaultProj, DEFAULT_SCREEN_DPI, resolution);
return scaleVal;
return Math.round(scaleVal ?? 0);
}

const leftCartographic = scene.globe.ellipsoid.cartesianToCartographic(leftPos);
Expand All @@ -394,7 +415,7 @@ export function getMapScaleForCesium(viewer) {
const geodesic = new Cesium.EllipsoidGeodesic(leftCartographic, rightCartographic);
const resolution = geodesic.surfaceDistance; // This is meters per 1 pixel [resolution]
const scaleValue = getScale(cesiumDefaultProj, DEFAULT_SCREEN_DPI, resolution);
return scaleValue;
return Math.round(scaleValue ?? 0);
}
/**
* get random coordinates within CRS extent
Expand Down
12 changes: 4 additions & 8 deletions web/client/utils/styleparser/OLStyleParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import { drawIcons } from './IconUtils';

import isString from 'lodash/isString';
import { geometryFunctionsLibrary } from './GeometryFunctionsUtils';
import { DEFAULT_SCREEN_DPI, getScale } from '../MapUtils';

const getGeometryFunction = geometryFunctionsLibrary.openlayers({
Point: OlGeomPoint,
Expand Down Expand Up @@ -443,14 +444,9 @@ export class OlStyleParser {
this._getMap = () => map;
const styles = [];

// calculate scale for resolution (from ol-util MapUtil)
const units = map
? map.getView().getProjection().getUnits()
: 'm';
const dpi = 25.4 / 0.28;
const mpu = METERS_PER_UNIT[units];
const inchesPerMeter = 39.37;
const scale = resolution * mpu * inchesPerMeter * dpi;
// instead of using ol0util MapUtil -> calc. scale value using getScale() to be matched with the predefined scale list values
const projection = map?.getView()?.getProjection()?.getCode() || "EPSG:3857";
const scale = Math.round(getScale(projection, DEFAULT_SCREEN_DPI, resolution));

rules.forEach((rule) => {
// handling scale denominator
Expand Down
2 changes: 1 addition & 1 deletion web/client/utils/styleparser/StyleParserUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ export const geoStylerScaleDenominatorFilter = (rule = {}, mapViewScale) => {
if (rule?.scaleDenominator && mapViewScale) {
const {min, max} = rule?.scaleDenominator;
if ((min !== undefined && mapViewScale < min) ||
(max !== undefined && mapViewScale > max)) {
(max !== undefined && mapViewScale >= max)) {
return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,11 @@ describe("StyleParserUtils ", () => {
// tests for geoStylerScaleDenominatorFilter
describe('geoStylerScaleDenominatorFilter', () => {

it('returns true when scale is within [min, max] range', () => {
it('returns true when scale is within [min, max] range -> max excluded and min included', () => {
const rule = { scaleDenominator: { min: 1000, max: 10000 } };
expect(geoStylerScaleDenominatorFilter(rule, 1000)).toBe(true); // min included
expect(geoStylerScaleDenominatorFilter(rule, 5000)).toBe(true);
expect(geoStylerScaleDenominatorFilter(rule, 1000)).toBe(true);
expect(geoStylerScaleDenominatorFilter(rule, 10000)).toBe(true);
expect(geoStylerScaleDenominatorFilter(rule, 10000)).toBe(false); // max is excluded
});

it('returns false when scale is outside [min, max] range', () => {
Expand Down Expand Up @@ -207,8 +207,8 @@ describe("StyleParserUtils ", () => {
color: "#3388FF",
scaleDenominator: { min: 2500, max: 25000 }
};
expect(geoStylerScaleDenominatorFilter(rule, 10000)).toBe(true);
expect(geoStylerScaleDenominatorFilter(rule, 1000)).toBe(false);
expect(geoStylerScaleDenominatorFilter(rule, 10000)).toBe(true);
expect(geoStylerScaleDenominatorFilter(rule, 50000)).toBe(false);
});
});
Expand Down
Loading