Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
162 changes: 96 additions & 66 deletions assets/src/components/Digitizing.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ export default class Digitizing extends HTMLElement {
this._availableTools = DigitizingAvailableTools.slice(1);
}

/**
* Show an editing message popup for the selected tool
* @param {string} messageKey - The lizDict key for the message
*/
_showEditingMessage(messageKey) {
const msg = lizDict[messageKey];
if (!msg) return;
// Remove any previous editing message
$('#lizmap-editing-message').remove();
lizMap.addMessage(msg, 'info', true, 10000).attr('id', 'lizmap-editing-message');
}

connectedCallback() {

// Update available tools from attribute
Expand Down Expand Up @@ -244,34 +256,45 @@ export default class Digitizing extends HTMLElement {
</form>
`;

const mainTemplate = (toolSelected) => html`
const mainTemplate = (toolSelected) => {
// Evaluate on every render so it reflects current edition state
const isEditionPoint = this.context === 'edition' && mainLizmap.edition?.layerGeometry === 'point';

// For point layers in edition, no toolbar needed — drawing starts automatically
if (isEditionPoint) {
this.style.display = 'none';
return html``;
}
this.style.display = '';

return html`
<div class="digitizing">
${toolButtonTemplate(this._availableTools, toolSelected)}
<input
${this.context !== 'edition' ? toolButtonTemplate(this._availableTools, toolSelected) : ''}
${this.context !== 'edition' ? html`<input
type="color"
class="digitizing-color btn"
.value="${mainLizmap.digitizing.drawColor}"
@input=${(event) => mainLizmap.digitizing._userChangedColor(event.target.value)}
data-bs-toggle="tooltip"
data-bs-title="${lizDict['digitizing.toolbar.color']}"
>
<button
>` : ''}
${this.context !== 'edition' ? html`<button
type="button"
class="digitizing-edit btn ${mainLizmap.digitizing.isEdited ? 'active btn-primary' : ''}"
?disabled=${!mainLizmap.digitizing.featureDrawn}
@click=${() => mainLizmap.digitizing.toggleEdit()}
@click=${() => { mainLizmap.digitizing.toggleEdit(); if (mainLizmap.digitizing.isEdited) this._showEditingMessage('digitizing.toolbar.edit.help'); }}
data-bs-toggle="tooltip"
data-bs-title="${lizDict['digitizing.toolbar.edit']}"
>
<svg>
<use href="${lizUrls.svgSprite}#edit"/>
</svg>
</button>
<button
</button>` : ''}
${!isEditionPoint ? html`<button
type="button"
class="digitizing-rotate btn ${mainLizmap.digitizing.isRotate ? 'active btn-primary' : ''}"
?disabled=${!mainLizmap.digitizing.featureDrawn}
@click=${() => mainLizmap.digitizing.toggleRotate()}
@click=${() => { mainLizmap.digitizing.toggleRotate(); if (mainLizmap.digitizing.isRotate) this._showEditingMessage('digitizing.toolbar.rotate.help'); }}
data-bs-toggle="tooltip"
data-bs-title="${lizDict['digitizing.toolbar.rotate']}"
>
Expand All @@ -283,27 +306,42 @@ export default class Digitizing extends HTMLElement {
type="button"
class="digitizing-scaling btn ${mainLizmap.digitizing.isScaling ? 'active btn-primary' : ''}"
?disabled=${!mainLizmap.digitizing.featureDrawn}
@click=${() => mainLizmap.digitizing.toggleScaling()}
@click=${() => { mainLizmap.digitizing.toggleScaling(); if (mainLizmap.digitizing.isScaling) this._showEditingMessage('digitizing.toolbar.scaling.help'); }}
data-bs-toggle="tooltip"
data-bs-title="${lizDict['digitizing.toolbar.scaling']}"
>
<svg>
<use href="${lizUrls.svgSprite}#scaling"/>
</svg>
</button>
<button
${this.context !== 'edition' ? html`<button
type="button"
class="digitizing-split btn ${mainLizmap.digitizing.isSplitting ? 'active btn-primary' : ''}"
?disabled=${!mainLizmap.digitizing.featureDrawn}
@click=${() => mainLizmap.digitizing.toggleSplit()}
@click=${() => { mainLizmap.digitizing.toggleSplit(); if (mainLizmap.digitizing.isSplitting) this._showEditingMessage('digitizing.toolbar.split.help'); }}
data-bs-toggle="tooltip"
data-bs-title="${lizDict['digitizing.toolbar.split']}"
>
<svg>
<use href="${lizUrls.svgSprite}#split"/>
</svg>
</button>
<button
</button>` : ''}` : ''}
${this.context === 'edition' && !isEditionPoint ? html`<lizmap-paste-geom></lizmap-paste-geom> <button
type="button"
class="digitizing-restart btn"
?disabled=${!mainLizmap.digitizing.featureDrawn}
@click=${() => {
if (!confirm(lizDict['edition.confirm.restart-drawing'])) return;
mainLizmap.digitizing.eraseAll();
const toolMap = { point: 'point', line: 'line', polygon: 'polygon' };
mainLizmap.digitizing.toolSelected = toolMap[mainLizmap.edition?.layerGeometry] || 'point';
}}
data-bs-toggle="tooltip"
data-bs-title="${lizDict['edition.toolbar.redraw']}"
>
<i class="icon-refresh"></i>
</button>` : ''}
${this.context !== 'edition' ? html`<button
type="button"
class="digitizing-erase btn ${mainLizmap.digitizing.isErasing ? 'active btn-primary' : ''}"
?disabled=${!mainLizmap.digitizing.featureDrawn}
Expand Down Expand Up @@ -336,8 +374,8 @@ export default class Digitizing extends HTMLElement {
data-bs-title="${lizDict['tree.button.checkbox']}"
>
<i class="icon-eye-${mainLizmap.digitizing.visibility ? 'open' : 'close'}"></i>
</button>
${this.measureAvailable ? measureButtonTemplate(
</button>` : ''}
${this.measureAvailable && !isEditionPoint ? measureButtonTemplate(
mainLizmap.digitizing.hasMeasureVisible,
) : ''}
${this.saveAvailable ? saveButtonTemplate(
Expand Down Expand Up @@ -413,65 +451,29 @@ export default class Digitizing extends HTMLElement {
<div class="digitizing-state hide">
<div class="digitizing-save-state hide">${lizDict['digitizing.toolbar.save.state']}</div>
</div>
<div class="digitizing-constraints ${mainLizmap.digitizing.hasConstraintsPanelVisible ? '' : 'hide'}">
<details>
<summary>
${lizDict['digitizing.constraint.title']}
</summary>
${lizDict['digitizing.constraint.details']}
</details>
<div class="digitizing-constraint-distance input-append">
<input
type="number"
placeholder="${lizDict['digitizing.constraint.distance']}"
class="distance form-control"
min="0"
step="any"
@input=${
event => mainLizmap.digitizing.distanceConstraint = event.target.value
}
>
<span class="add-on">m</span>
</div>
<div class="digitizing-constraint-angle input-append">
<input
type="number"
placeholder="${lizDict['digitizing.constraint.angle']}"
class="angle form-control"
step="any"
@input=${
event => mainLizmap.digitizing.angleConstraint = event.target.value
}
>
<span class="add-on">°</span>
</div>
</div>
${this.textToolsAvailable ? textToolsTemplate(
mainLizmap.digitizing.editedFeatures.length != 0
) : ''}
</div>`;
};

render(
mainTemplate(
this.toolSelected,
),
this,
);

const tooltipTriggerList = this.querySelectorAll('[data-bs-toggle="tooltip"]');
[...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl, {
trigger: 'hover'
}));
this._renderTemplate = () => {
render(mainTemplate(this.toolSelected), this);
this._initTooltips();
this._initDropdowns();
};

mainEventDispatcher.addListener(
() => {
// Sync component tool state with module when context matches
if (mainLizmap.digitizing.context === this.context) {
const moduleTool = mainLizmap.digitizing.toolSelected;
if (this._availableTools.includes(moduleTool)) {
this._toolSelected = moduleTool;
}
}
if (!this.disabled) {
render(
mainTemplate(
this.toolSelected,
),
this,
);
this._renderTemplate();
}
},
[
Expand All @@ -495,11 +497,39 @@ export default class Digitizing extends HTMLElement {
'digitizing.visibility',
]
);

this._renderTemplate();
}

disconnectedCallback() {
}


_initTooltips() {
// Dispose existing tooltips to avoid duplicates
this.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => {
// Skip elements whose title resolves to null (e.g. missing lizDict key)
// to prevent Bootstrap from throwing a type-check error.
const title = el.getAttribute('data-bs-title') || el.getAttribute('title');
if (!title) return;
const existing = bootstrap.Tooltip.getInstance(el);
if (existing) existing.dispose();
new bootstrap.Tooltip(el, { trigger: 'hover' });
});
}

_initDropdowns() {
// Use strategy:'fixed' so Popper positions the dropdown relative to the
// viewport, allowing it to escape overflow:auto containers (#mini-dock).
this.querySelectorAll('[data-bs-toggle="dropdown"]').forEach(el => {
if (!bootstrap.Dropdown.getInstance(el)) {
new bootstrap.Dropdown(el, {
popperConfig: { strategy: 'fixed' }
});
}
});
}

/**
* Digitizing context
* The element attribute: context
Expand Down
26 changes: 7 additions & 19 deletions assets/src/components/Snapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,12 @@ export default class Snapping extends HTMLElement {
<h3>${lizDict['snapping.title']}</h3>
<div class="form-group">
<div class="controls">
<div class="btn-group">
<button
class="btn ${mainLizmap.snapping.active ? 'active btn-success' : ''}"
@click=${() => mainLizmap.snapping.toggle()}
>
${mainLizmap.snapping.active ? lizDict['geolocate.toolbar.stop'] : lizDict['geolocate.toolbar.start']}
</button>
<button
class="btn ${mainLizmap.snapping._snapLayersRefreshable ? 'btn-warning' : ''}"
?disabled=${!mainLizmap.snapping._snapLayersRefreshable}
@click=${() => mainLizmap.snapping.getSnappingData() }
>
<svg width="14" height="14">
<use href="${lizUrls.svgSprite}#refresh"/>
</svg>
</button>
</div>
<button
class="btn ${mainLizmap.snapping.active ? 'active btn-success' : ''}"
@click=${() => mainLizmap.snapping.toggle()}
>
${mainLizmap.snapping.active ? lizDict['geolocate.toolbar.stop'] : lizDict['geolocate.toolbar.start']}
</button>
</div>
${mainLizmap.snapping.active ?
html`<div class="snap-panel-controls">
Expand Down Expand Up @@ -91,8 +80,7 @@ export default class Snapping extends HTMLElement {
},
[
'snapping.config',
'snapping.active',
'snapping.refreshable'
'snapping.active'
]
);
}
Expand Down
39 changes: 23 additions & 16 deletions assets/src/components/edition/ReverseGeom.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* @license MPL-2.0
*/

import { mainLizmap } from '../../modules/Globals.js';
import { mainLizmap, mainEventDispatcher } from '../../modules/Globals.js';

/**
* Web component used to reverse vertices order for a modified feature
Expand All @@ -25,25 +25,32 @@ export default class reverseGeom extends HTMLElement {
}

_reverse(){
if (!mainLizmap.edition.modifyFeatureControl
|| !mainLizmap.edition.modifyFeatureControl.active
|| mainLizmap.edition.modifyFeatureControl.vertices.length == 0){
return;
}

const lonLat = [];
// OL10 path: operate on the feature currently in the digitizing draw layer.
// (The legacy OL2 modifyFeatureControl is no longer activated.)
const features = mainLizmap.digitizing?.featureDrawn;
if (!features || features.length === 0) return;

for (const vertice of mainLizmap.edition.modifyFeatureControl.vertices) {
lonLat.push([vertice.geometry.x, vertice.geometry.y]);
}

lonLat.reverse();
const feature = features[0];
const geom = feature.getGeometry();
const type = geom.getType();

for (let index = 0; index < lonLat.length; index++) {
mainLizmap.edition.modifyFeatureControl.vertices[index].move(new OpenLayers.LonLat(lonLat[index][0], lonLat[index][1]));
if (type === 'LineString' || type === 'MultiPoint') {
geom.setCoordinates(geom.getCoordinates().slice().reverse());
} else if (type === 'Polygon') {
geom.setCoordinates(geom.getCoordinates().map(ring => ring.slice().reverse()));
} else if (type === 'MultiLineString') {
geom.setCoordinates(geom.getCoordinates().map(line => line.slice().reverse()));
} else if (type === 'MultiPolygon') {
geom.setCoordinates(
geom.getCoordinates().map(poly => poly.map(ring => ring.slice().reverse()))
);
} else {
// Point geometries have nothing to reverse.
return;
}

mainLizmap.edition.modifyFeatureControl.layer.events.triggerEvent("featuremodified", { feature: mainLizmap.edition.modifyFeatureControl.feature });
// Sync the hidden geom form field via the digitizing change event.
mainEventDispatcher.dispatch('digitizing.geometryChanged');

// Tell user reverse is done
lizMap.addMessage(lizDict['edition.revertgeom.success'], 'success', true).attr('id', 'lizmap-edition-message');
Expand Down
Loading
Loading