diff --git a/.env b/.env
index 2fd995d3..5074dd0f 100644
--- a/.env
+++ b/.env
@@ -35,3 +35,4 @@ VITE_MODE_LIB=true
# MyMaps / Draw
VITE_MYMAPS_URL="/mymaps"
VITE_ARROW_MODEL_URL="/static-ngeo/models/arrow5.glb"
+VITE_ELEVATION_URL="/raster"
diff --git a/.env.development b/.env.development
index ec143a9c..d9c0ae14 100644
--- a/.env.development
+++ b/.env.development
@@ -34,4 +34,5 @@ VITE_MODE_LIB=false
# MyMaps / Draw
VITE_MYMAPS_URL="https://migration.geoportail.lu/mymaps"
-VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5.glb"
\ No newline at end of file
+VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5.glb"
+VITE_ELEVATION_URL="https://migration.geoportail.lu/raster"
\ No newline at end of file
diff --git a/.env.e2e b/.env.e2e
index 59583476..3d4d87ac 100644
--- a/.env.e2e
+++ b/.env.e2e
@@ -35,3 +35,4 @@ VITE_MODE_LIB=false
# MyMaps / Draw
VITE_MYMAPS_URL="https://migration.geoportail.lu/mymaps"
VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5.glb"
+VITE_ELEVATION_URL="https://migration.geoportail.lu/raster"
diff --git a/.env.staging b/.env.staging
index 619afc3b..cb0d2ca3 100644
--- a/.env.staging
+++ b/.env.staging
@@ -34,4 +34,5 @@ VITE_MODE_LIB=true
# MyMaps / Draw
VITE_MYMAPS_URL="https://migration.geoportail.lu/mymaps"
-VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5.glb"
\ No newline at end of file
+VITE_ARROW_MODEL_URL="https://migration.geoportail.lu/static-ngeo/models/arrow5.glb"
+VITE_ELEVATION_URL="/raster"
\ No newline at end of file
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 3e43c577..d91cadeb 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -16,6 +16,7 @@ module.exports = {
extends: ['plugin:cypress/recommended'],
},
],
+ plugins: ['no-only-tests'],
rules: {
'no-prototype-builtins': 'off',
'@typescript-eslint/ban-types': 'off',
@@ -28,6 +29,7 @@ module.exports = {
'@typescript-eslint/no-unused-vars': ['error'],
'import/no-unresolved': 'off',
'no-console': 'error',
+ 'no-only-tests/no-only-tests': 'error',
},
parserOptions: {
ecmaVersion: 'latest',
diff --git a/README.md b/README.md
index e0b2642a..9045d200 100644
--- a/README.md
+++ b/README.md
@@ -45,7 +45,7 @@ A `.prettierignore` file is used by prettier and lint to exclude some files (suc
### End to end testing with Cypress
-For e2e tests, the code is instrumented with the Istanbul library to obtain resulting code coverage. The dev test server is launched with the env var `INSRUMENT_COVERAGE=true`
+For e2e tests, the code is instrumented with the Istanbul library to obtain resulting code coverage. The dev test server is launched with the env var `INSTRUMENT_COVERAGE=true`
- `test:e2e` will launch e2e tests and opens Cypress UI. You need to build the app before using this command with `npm run build-only -- --mode=e2e`. This will test the app built for production (with css minification, images src optimization using base64, ...)
- `test:e2e:ci` This will run tests in command line only on a dev build (no need to build the app). Used by ci workflow.
diff --git a/cypress/e2e/draw/draw-feat-line.cy.ts b/cypress/e2e/draw/draw-feat-line.cy.ts
index 00558d4d..14434710 100644
--- a/cypress/e2e/draw/draw-feat-line.cy.ts
+++ b/cypress/e2e/draw/draw-feat-line.cy.ts
@@ -27,6 +27,12 @@ describe('Draw "Line"', () => {
testFeatItemMeasurements()
})
+ it('updates length measurement when editing geometry', () => {
+ cy.get('*[data-cy="featItemLength"]').should('contain.text', '55.4 km')
+ cy.dragVertexOnMap(200, 200, 300, 300)
+ cy.get('*[data-cy="featItemLength"]').should('contain.text', '111 km')
+ })
+
it('displays the possible actions for the feature', () => {
testFeatItem()
})
diff --git a/cypress/e2e/draw/draw-feat-point.cy.ts b/cypress/e2e/draw/draw-feat-point.cy.ts
index ec6dac22..92525e23 100644
--- a/cypress/e2e/draw/draw-feat-point.cy.ts
+++ b/cypress/e2e/draw/draw-feat-point.cy.ts
@@ -21,6 +21,32 @@ function testFeatStyleEditionTabContent() {
describe('Draw "Point"', () => {
beforeEach(() => {
+ // mocks for 100x100, 200x200, 300x300 clicks
+ cy.intercept(
+ 'GET',
+ '/raster?lon=-25877.619036593664&lat=154433.4715351454',
+ {
+ statusCode: 200,
+ body: {
+ dhm: null,
+ },
+ }
+ )
+ cy.intercept(
+ 'GET',
+ '/raster?lon=12756.103097272688&lat=114635.74032468312',
+ {
+ statusCode: 200,
+ body: {
+ dhm: 333.13,
+ },
+ }
+ )
+ cy.intercept('GET', '/raster?lon=51966.98676810359&lat=74839.09999860045', {
+ statusCode: 500,
+ body: {},
+ })
+
cy.visit('/')
cy.get('button[data-cy="drawButton"]').click()
cy.get('button[data-cy="drawPointButton"]').click()
@@ -36,6 +62,27 @@ describe('Draw "Point"', () => {
testFeatItemMeasurements()
})
+ it('displays N/A elevation for Point if data is null', () => {
+ cy.get('*[data-cy="featItemElevation"]').should('contain.text', 'N/A')
+ })
+
+ it('displays elevation for new Point', () => {
+ cy.get('button[data-cy="drawPointButton"]').click()
+ cy.get('button[data-cy="drawPointButton"]').click()
+ cy.get('div.ol-viewport').click(200, 200)
+ cy.get('*[data-cy="featItemElevation"]').should(
+ 'contain.text',
+ '333.13 m'
+ )
+ })
+
+ it('displays N/A elevation for new Point if response has error', () => {
+ cy.get('button[data-cy="drawPointButton"]').click()
+ cy.get('button[data-cy="drawPointButton"]').click()
+ cy.get('div.ol-viewport').click(300, 300)
+ cy.get('*[data-cy="featItemElevation"]').should('contain.text', 'N/A')
+ })
+
it('displays the possible actions for the feature', () => {
testFeatItem()
})
diff --git a/cypress/e2e/draw/draw-feat-polygon.cy.ts b/cypress/e2e/draw/draw-feat-polygon.cy.ts
index 24194748..ee8ae345 100644
--- a/cypress/e2e/draw/draw-feat-polygon.cy.ts
+++ b/cypress/e2e/draw/draw-feat-polygon.cy.ts
@@ -27,6 +27,14 @@ describe('Draw "Polygon"', () => {
testFeatItemMeasurements()
})
+ it('updates length and area measurements when editing geometry', () => {
+ cy.get('*[data-cy="featItemLength"]').should('contain.text', '134 km')
+ cy.get('*[data-cy="featItemArea"]').should('contain.text', '766 km²')
+ cy.dragVertexOnMap(200, 200, 300, 300)
+ cy.get('*[data-cy="featItemLength"]').should('contain.text', '238 km')
+ cy.get('*[data-cy="featItemArea"]').should('contain.text', '1530 km²')
+ })
+
it('displays the possible actions for the feature', () => {
testFeatItem()
})
diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts
index 8cfa2d1d..8723a30a 100644
--- a/cypress/support/commands.ts
+++ b/cypress/support/commands.ts
@@ -1,6 +1,7 @@
///
import type { Interaction } from 'ol/interaction'
+import 'cypress-real-events/support'
// ***********************************************
// This example commands.ts shows you how to
@@ -61,11 +62,21 @@ Cypress.Commands.add('getModifyInteraction', () => {
})
})
+Cypress.Commands.add('dragVertexOnMap', (originX, originY, x, y) => {
+ cy.get('div.ol-viewport').realMouseDown({
+ x: originX,
+ y: originY,
+ })
+ cy.get('div.ol-viewport').realMouseMove(x, y)
+ cy.get('div.ol-viewport').realMouseUp()
+})
+
declare global {
namespace Cypress {
interface Chainable {
getDrawInteractions(): Chainable>
getModifyInteraction(): Chainable
+ dragVertexOnMap(): Chainable
// login(email: string, password: string): Chainable
// drag(subject: string, options?: Partial): Chainable
// dismiss(subject: string, options?: Partial): Chainable
diff --git a/package-lock.json b/package-lock.json
index c107b648..e4eeef61 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -55,8 +55,10 @@
"@vue/tsconfig": "^0.1.3",
"core-js": "^3.37.1",
"cypress": "^13.7.2",
+ "cypress-real-events": "^1.13.0",
"eslint": "^8.22.0",
"eslint-plugin-cypress": "^2.12.1",
+ "eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-vue": "^9.3.0",
"husky": "^8.0.2",
"i18next": "^22.0.6",
@@ -5665,6 +5667,15 @@
"node": "^16.0.0 || ^18.0.0 || >=20.0.0"
}
},
+ "node_modules/cypress-real-events": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/cypress-real-events/-/cypress-real-events-1.13.0.tgz",
+ "integrity": "sha512-LoejtK+dyZ1jaT8wGT5oASTPfsNV8/ClRp99ruN60oPj8cBJYod80iJDyNwfPAu4GCxTXOhhAv9FO65Hpwt6Hg==",
+ "dev": true,
+ "peerDependencies": {
+ "cypress": "^4.x || ^5.x || ^6.x || ^7.x || ^8.x || ^9.x || ^10.x || ^11.x || ^12.x || ^13.x"
+ }
+ },
"node_modules/cypress/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -7115,6 +7126,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/eslint-plugin-no-only-tests": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-3.3.0.tgz",
+ "integrity": "sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=5.0.0"
+ }
+ },
"node_modules/eslint-plugin-prettier": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
diff --git a/package.json b/package.json
index 8b7f13de..2fd96403 100644
--- a/package.json
+++ b/package.json
@@ -14,9 +14,9 @@
"test": "npm run test:unit",
"test:unit": "vitest --environment jsdom --root .",
"test:unit:ci": "vitest run --environment jsdom --coverage",
- "test:e2e": "INSRUMENT_COVERAGE=true start-server-and-test preview :4173 'cypress open --e2e'",
- "test:e2e:ci": "INSRUMENT_COVERAGE=true start-server-and-test 'VITE_USE_PROXYURL=false vite dev --port 4173' :4173 'cypress run --e2e'",
- "test:e2e:dev": "INSRUMENT_COVERAGE=true start-server-and-test 'vite dev --port 4173' :4173 'cypress open --e2e'",
+ "test:e2e": "INSTRUMENT_COVERAGE=true start-server-and-test preview :4173 'cypress open --e2e'",
+ "test:e2e:ci": "INSTRUMENT_COVERAGE=true start-server-and-test 'VITE_USE_PROXYURL=false vite dev --port 4173' :4173 'cypress run --e2e'",
+ "test:e2e:dev": "INSTRUMENT_COVERAGE=true start-server-and-test 'vite dev --port 4173' :4173 'cypress open --e2e'",
"coverage-report": "nyc report",
"type-check:dev": "npm run type-check -- --watch",
"type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
@@ -78,8 +78,10 @@
"@vue/tsconfig": "^0.1.3",
"core-js": "^3.37.1",
"cypress": "^13.7.2",
+ "cypress-real-events": "^1.13.0",
"eslint": "^8.22.0",
"eslint-plugin-cypress": "^2.12.1",
+ "eslint-plugin-no-only-tests": "^3.3.0",
"eslint-plugin-vue": "^9.3.0",
"husky": "^8.0.2",
"i18next": "^22.0.6",
diff --git a/src/bundle/lib.ts b/src/bundle/lib.ts
index eb6479a4..3f9819f4 100644
--- a/src/bundle/lib.ts
+++ b/src/bundle/lib.ts
@@ -49,6 +49,7 @@ import { clearLayersCache } from '@/stores/layers.cache'
import i18next, { InitOptions } from 'i18next'
import backend from 'i18next-http-backend'
import I18NextVue from 'i18next-vue'
+import formatDistanceDirective from './directives/format-distance.directive'
import App from '../App.vue'
@@ -78,6 +79,7 @@ export default function useLuxLib(options: LuxLibOptions) {
app.use(createPinia())
app.use(I18NextVue, { i18next })
app.use(VueDOMPurifyHTML)
+ app.use(formatDistanceDirective)
const createElementInstance = (component = {}, app = null) => {
return defineCustomElement(
diff --git a/src/components/draw/feature-item.vue b/src/components/draw/feature-item.vue
index 82936326..ccd9004b 100644
--- a/src/components/draw/feature-item.vue
+++ b/src/components/draw/feature-item.vue
@@ -84,7 +84,6 @@ function onSubmitEditInfo() {
emit('toggleDock')"
>
@@ -95,7 +94,6 @@ function onSubmitEditInfo() {
emit('toggleDock')"
@clickDelete="onClickDelete"
diff --git a/src/components/draw/feature-measurements-helper.ts b/src/components/draw/feature-measurements-helper.ts
index 68536d6d..044f3d01 100644
--- a/src/components/draw/feature-measurements-helper.ts
+++ b/src/components/draw/feature-measurements-helper.ts
@@ -1,5 +1,11 @@
import { PointData } from '@/components/common/graph/d3-graph-elevation.d'
+import { Coordinate } from 'ol/coordinate'
+import { transform } from 'ol/proj'
+import { debounceAsync } from '@/services/utils'
+const ELEVATION_URL = import.meta.env.VITE_ELEVATION_URL
+const MAP_CRS = 'EPSG:3857'
+const ELEVATION_CRS = 'EPSG:2169'
/**
* Request the csv profile with the current profile data.
* @param profileData The current profile data
@@ -9,3 +15,21 @@ export function downloadCsv(profileData: PointData[]) {
return
}
}
+
+export const getElevation = async (coordinate: Coordinate) => {
+ const lonlat = transform(coordinate, MAP_CRS, ELEVATION_CRS)
+ try {
+ const response = await fetch(
+ `${ELEVATION_URL}?lon=${lonlat[0]}&lat=${lonlat[1]}`
+ )
+ if (!response.ok) {
+ throw new Error('Network response was not ok')
+ }
+ const data = await response.json()
+ return data.dhm
+ } catch (error) {
+ return null //return null on error, like API when no data
+ }
+}
+
+export const getDebouncedElevation = debounceAsync(getElevation, 300)
diff --git a/src/components/draw/feature-measurements.vue b/src/components/draw/feature-measurements.vue
index 6d84ea0f..d01b7999 100644
--- a/src/components/draw/feature-measurements.vue
+++ b/src/components/draw/feature-measurements.vue
@@ -1,22 +1,62 @@