Skip to content

Commit

Permalink
Merge pull request #130 from Geoportail-Luxembourg/GSLUX-703-draw-too…
Browse files Browse the repository at this point in the history
…ltip

GSLUX-703: Add draw tooltip
  • Loading branch information
tkohr authored Aug 2, 2024
2 parents 7223361 + 5df417b commit c8b0be7
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 11 deletions.
47 changes: 42 additions & 5 deletions cypress/e2e/draw-bar.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ describe('Map controls', () => {
})

describe('when clicking button to draw point', () => {
it('activates ol Draw interaction in mode Point', () => {
beforeEach(() => {
cy.get('button[data-cy="drawPointButton"]').click()
})
it('activates ol Draw interaction in mode Point', () => {
cy.getDrawInteractions().then(drawInteractions => {
const activeInteractions = drawInteractions.filter(interaction => {
return interaction.getActive() === true
Expand All @@ -15,10 +17,16 @@ describe('Map controls', () => {
expect(activeInteractions[0].mode_).to.be.equal('Point')
})
})
it('does not display a tooltip overlay when clicking on the map', () => {
cy.get('div.ol-viewport').click(100, 100)
cy.get('div.lux-tooltip').should('not.exist')
})
})
describe('when clicking button to draw label', () => {
it('activates ol Draw interaction in mode Point', () => {
beforeEach(() => {
cy.get('button[data-cy="drawLabelButton"]').click()
})
it('activates ol Draw interaction in mode Point', () => {
cy.getDrawInteractions().then(drawInteractions => {
const activeInteractions = drawInteractions.filter(interaction => {
return interaction.getActive() === true
Expand All @@ -27,10 +35,16 @@ describe('Map controls', () => {
expect(activeInteractions[0].mode_).to.be.equal('Point')
})
})
it('does not display a tooltip overlay when clicking on the map', () => {
cy.get('div.ol-viewport').click(100, 100)
cy.get('div.lux-tooltip').should('not.exist')
})
})
describe('when clicking button to draw line', () => {
it('activates ol Draw interaction in mode LineString', () => {
beforeEach(() => {
cy.get('button[data-cy="drawLineButton"]').click()
})
it('activates ol Draw interaction in mode LineString', () => {
cy.getDrawInteractions().then(drawInteractions => {
const activeInteractions = drawInteractions.filter(interaction => {
return interaction.getActive() === true
Expand All @@ -39,10 +53,18 @@ describe('Map controls', () => {
expect(activeInteractions[0].mode_).to.be.equal('LineString')
})
})
it('displays a tooltip when clicking on the map and hides it on draw end (second point double click)', () => {
cy.get('div.ol-viewport').click(100, 100)
cy.get('div.lux-tooltip').should('exist')
cy.get('div.ol-viewport').dblclick(200, 200)
cy.get('div.lux-tooltip').should('not.exist')
})
})
describe('when clicking button to draw polygon', () => {
it('activates ol Draw interaction in mode Polygon', () => {
beforeEach(() => {
cy.get('button[data-cy="drawPolygonButton"]').click()
})
it('activates ol Draw interaction in mode Polygon', () => {
cy.getDrawInteractions().then(drawInteractions => {
const activeInteractions = drawInteractions.filter(interaction => {
return interaction.getActive() === true
Expand All @@ -51,10 +73,19 @@ describe('Map controls', () => {
expect(activeInteractions[0].mode_).to.be.equal('Polygon')
})
})
it('displays a tooltip when clicking on the map and hides it on draw end (third point double click)', () => {
cy.get('div.ol-viewport').click(100, 100)
cy.get('div.lux-tooltip').should('exist')
cy.get('div.ol-viewport').click(100, 200)
cy.get('div.ol-viewport').dblclick(200, 200)
cy.get('div.lux-tooltip').should('not.exist')
})
})
describe('when clicking button to draw circle', () => {
it('activates ol Draw interaction in mode Circle', () => {
beforeEach(() => {
cy.get('button[data-cy="drawCircleButton"]').click()
})
it('activates ol Draw interaction in mode Circle', () => {
cy.getDrawInteractions().then(drawInteractions => {
const activeInteractions = drawInteractions.filter(interaction => {
return interaction.getActive() === true
Expand All @@ -63,5 +94,11 @@ describe('Map controls', () => {
expect(activeInteractions[0].mode_).to.be.equal('Circle')
})
})
it('displays a tooltip when clicking on the map and hides it on draw end (second point simple click)', () => {
cy.get('div.ol-viewport').click(100, 100)
cy.get('div.lux-tooltip').should('exist')
cy.get('div.ol-viewport').click(200, 200)
cy.get('div.lux-tooltip').should('not.exist')
})
})
})
6 changes: 6 additions & 0 deletions cypress/e2e/footer-bar.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ describe('Footer bar', () => {
cy.get('button[data-cy="drawLineButton"]').click()
cy.get('[data-cy="followRoads"]').should('not.exist')
})

it('opens the MyMaps panel', () => {
cy.get('[data-cy="myMapsPanel"]').should('not.exist')
cy.get('button[data-cy="drawButton"]').click()
cy.get('[data-cy="myMapsPanel"]').should('exist')
})
})

describe('Layers button', () => {
Expand Down
4 changes: 4 additions & 0 deletions src/assets/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@
.lux-time-datepicker {
@apply border-[#767676] border-[1px] pl-1;
}

.lux-tooltip {
@apply relative bg-black bg-opacity-50 rounded text-white p-1.5 opacity-70 whitespace-nowrap;
}
}

.fa-solid {
Expand Down
12 changes: 6 additions & 6 deletions src/composables/draw/draw-interaction.composable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@ import { listen } from 'ol/events.js'
import Draw, { DrawEvent, Options } from 'ol/interaction/Draw'
import useMap from '@/composables/map/map.composable'
import useDrawnFeatures from './drawn-features.composable'
import drawTooltip from './draw-tooltip'

export default function useDrawInteraction(options: Options) {
const drawInteraction = new Draw(options)
drawInteraction.setActive(false)
const map = useMap().getOlMap()
map.addInteraction(drawInteraction)

//if the listens becomes too specific by geometry type, move back to draw.composable or split into multiple composables by geom type
// listen(drawInteraction, 'drawstart', event => {
// // todo: implement measure tooltip (createMeasureTooltip_) per geometry type (see e.g. onDrawLineStart_)
// })

listen(drawInteraction, 'drawstart', event =>
drawTooltip.add(map, event as DrawEvent)
)
listen(drawInteraction, 'drawend', event => {
onDrawEnd(event as DrawEvent)
})
const { addFeature } = useDrawnFeatures()
function onDrawEnd(event: DrawEvent) {
//todo: this.removeMeasureTooltip_();
drawTooltip.remove()
addFeature(event.feature)
//TODO: migrate rest of DRawController.onDrawEnd_
}

return {
Expand Down
129 changes: 129 additions & 0 deletions src/composables/draw/draw-tooltip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { Overlay } from 'ol'
import { EventsKey, listen } from 'ol/events'
import { Projection, transform } from 'ol/proj'
import { getDistance as haversineDistance, getArea } from 'ol/sphere'
import { unByKey } from 'ol/Observable'
import { Circle, Geometry, LineString, Polygon } from 'ol/geom'
import OlMap from 'ol/Map'
import { DrawEvent } from 'ol/interaction/Draw'

class DrawTooltip {
private measureTooltipElement: HTMLElement | null = null
private measureTooltipOverlay: Overlay | null = null
private changeEventKey: EventsKey | null = null

public add(map: OlMap, event: DrawEvent) {
this.createMeasureTooltip(map)
const geometry = event.feature.getGeometry()
console.assert(geometry !== undefined)
if (geometry === undefined) return
const proj = map.getView().getProjection()

this.changeEventKey = listen(
geometry,
'change',
() => this.updateTootip(geometry, proj),
this
)
}

public remove() {
this.removeMeasureTooltip()

if (this.changeEventKey !== null) {
unByKey(this.changeEventKey)
this.changeEventKey = null
}
}

private createMeasureTooltip(map: OlMap) {
this.removeMeasureTooltip()
this.measureTooltipElement = document.createElement('div')
this.measureTooltipElement.classList.add('lux-tooltip')

this.measureTooltipOverlay = new Overlay({
element: this.measureTooltipElement,
offset: [0, -15],
positioning: 'bottom-center',
stopEvent: false,
})
map.addOverlay(this.measureTooltipOverlay)
}

private removeMeasureTooltip() {
if (this.measureTooltipElement !== null) {
this.measureTooltipElement.parentNode?.removeChild(
this.measureTooltipElement
)
this.measureTooltipElement = null
this.measureTooltipOverlay = null
}
}

private updateTootip(geometry: Geometry, proj: Projection) {
let coord = undefined
let output = ''
if (geometry.getType() === 'LineString') {
const geom = geometry as LineString
coord = geom.getLastCoordinate()
if (coord !== null) {
output = this.getFormattedLength(geom, proj)
}
} else if (geometry.getType() === 'Polygon') {
const geom = geometry as Polygon
const verticesCount = geom.getCoordinates()[0].length
if (verticesCount > 2) {
coord = geom.getInteriorPoint().getCoordinates()
}
if (coord !== null) {
output = this.getFormattedArea(geom)
}
} else if (geometry.getType() === 'Circle') {
const geom = geometry as Circle
coord = geom.getLastCoordinate()
const center = geom.getCenter()
if (center !== null && coord !== null) {
output = this.getFormattedLength(new LineString([center, coord]), proj)
}
}
if (this.measureTooltipElement) {
this.measureTooltipElement.innerHTML = output
this.measureTooltipOverlay?.setPosition(coord)
}
}

private getFormattedLength(
lineString: LineString,
projection: Projection
): string {
let length = 0
const coordinates = lineString.getCoordinates()
for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) {
const c1 = transform(coordinates[i], projection, 'EPSG:4326')
const c2 = transform(coordinates[i + 1], projection, 'EPSG:4326')
length += haversineDistance(c1, c2)
}
let output
if (length > 1000) {
output = parseFloat((length / 1000).toPrecision(3)) + ' ' + 'km'
} else {
output = parseFloat(length.toPrecision(3)) + ' ' + 'm'
}
return output
}

private getFormattedArea(polygon: Polygon): string {
const area = Math.abs(getArea(polygon))
let output = ''
if (area > 1000000) {
output =
parseFloat((area / 1000000).toPrecision(3)) + ' ' + 'km<sup>2</sup>'
} else {
output = parseFloat(area.toPrecision(3)) + ' ' + 'm<sup>2</sup>'
}
return output
}
}

const drawTooltip = new DrawTooltip()
export default drawTooltip
6 changes: 6 additions & 0 deletions src/stores/app.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ export const useAppStore = defineStore(

function setDrawToolbarOpen(open: boolean) {
drawToolbarOpen.value = open

if (drawToolbarOpen.value) {
myMapsOpen.value = true
styleEditorOpen.value = false
layersOpen.value = false
}
}

function toggleMyMapsOpen(open?: boolean) {
Expand Down

0 comments on commit c8b0be7

Please sign in to comment.