StorymapperArrow/Drawing.js
2025-07-24 13:05:11 +02:00

296 lines
11 KiB
JavaScript

import { getArrowPolygon } from "athena-utils/shape/Arrow.js";
import { getFrontline } from "./Frontline.js";
import { LEFT_SIDE, RIGHT_SIDE, BOTH_SIDES } from "athena-utils/shape/Frontline.js";
import { handleDraw, handleClick, showArrowEditor, hideArrowEditor, showFrontlineEditor, hideFrontlineEditor, handleDelete, drawOnMap } from "./DrawingFunctions.js";
import { arrowParamsMap, frontlineParamsMap, currentFeature, setCurrentFeature} from "./DrawingFunctions.js";
export const ARROW = "arrow";
export const FRONTLINE = "frontline";
export const ADDITIONAL_SIDE = "additionalSide"; // used for Id in BOTH_SIDES style
let currentDrawStyle = ARROW;
mapboxgl.accessToken = 'pk.eyJ1Ijoib3V0ZG9vcm1hcHBpbmdjb21wYW55IiwiYSI6ImNqYmh3cDdjYzNsMnozNGxsYzlvMmk2bTYifQ.QqcZ4LVoLWnXafXdjZxnZg';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v12',
center: [10, 50],
zoom: 5
});
// Drawing visuals
const draw = new MapboxDraw({
displayControlsDefault: false,
controls: {
line_string: true
},
defaultMode: 'draw_line_string',
styles: [
// Main drawing line
{
id: 'gl-draw-line',
type: 'line',
filter: ['all', ['==', '$type', 'LineString'], ['==', 'active', 'true']],
layout: {
'line-cap': 'round',
'line-join': 'round'
},
paint: {
'line-color': 'rgb(0, 255, 0)',
'line-width': 4
}
},
// This makes the nonstop visible line invisible
{
id: 'gl-draw-line-inactive',
type: 'line',
filter: ['all', ['==', '$type', 'LineString'], ['==', 'deactive', 'false']],
layout: {
'line-cap': 'round',
'line-join': 'round'
},
paint: {
'line-color': 'rgba(0, 0, 0, 0)',
'line-width': 2
}
},
// Selected point
{
id: 'gl-draw-polygon-and-line-vertex-active',
type: 'circle',
filter: ['all', ['==', '$type', 'Point'], ['==', 'meta', 'vertex'], ['==', 'active', 'true']],
paint: {
'circle-radius': 6,
'circle-color': 'rgb(255, 255, 0)'
}
},
// Main points
{
id: 'gl-draw-polygon-and-line-vertex-inactive',
type: 'circle',
filter: ['all', ['==', '$type', 'Point'], ['==', 'meta', 'vertex'], ['==', 'active', 'false']],
paint: {
'circle-radius': 4,
'circle-color': 'rgb(0, 255, 0)'
}
},
// Midpoints
{
id: 'gl-draw-polygon-midpoint',
type: 'circle',
filter: ['all', ['==', '$type', 'Point'], ['==', 'meta', 'midpoint']],
paint: {
'circle-radius': 4,
'circle-color': 'rgb(0, 255, 174)'
}
}
]
});
// Map controlls
map.addControl(draw, 'top-left');
map.on('draw.create', (e) => {
handleDraw(e, map, draw, currentDrawStyle)
});
map.on('draw.update', (e) => {
handleDraw(e, map, draw, currentDrawStyle)
});
map.on('click', (e) => {
hideArrowEditor();
hideFrontlineEditor();
handleClick(e, ARROW, arrowParamsMap, showArrowEditor, map, draw, currentDrawStyle);
handleClick(e, FRONTLINE, frontlineParamsMap, showFrontlineEditor, map, draw, currentDrawStyle);
});
map.dragPan.enable();
export function updateButtonStyles(drawStyle) {
currentDrawStyle = drawStyle;
const buttonsContainer = document.getElementById('drawStyleButtons');
Array.from(buttonsContainer.querySelectorAll('button')).forEach(btn => {
const isSelected = btn.getAttribute('data-style') === drawStyle;
btn.style.backgroundColor = isSelected ? '#333' : '#f0f0f0';
btn.style.color = isSelected ? '#fff' : '#000';
});
}
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('removeArrow').addEventListener('click', () => {
handleDelete(ARROW, map, draw);
});
document.getElementById('removeFrontline').addEventListener('click', () => {
handleDelete(FRONTLINE, map, draw);
});
document.getElementById('applyArrowChanges').addEventListener('click', () => {
if (!currentFeature) return;
const coords = currentFeature.geometry.coordinates;
const id = currentFeature.id;
const splineStep = parseFloat(document.getElementById('splineStep').value);
const offsetDistance = parseFloat(document.getElementById('offsetDistance').value);
const range = parseFloat(document.getElementById('range').value);
const minValue = parseFloat(document.getElementById('minValue').value);
const widthArrow = parseFloat(document.getElementById('widthArrow').value);
const lengthArrow = parseFloat(document.getElementById('lengthArrow').value);
const calculation = parseInt(document.getElementById('styleArrow').value);
const fillColor = document.getElementById('arrowFillColor').value;
const outlineColor = document.getElementById('arrowOutlineColor').value;
const opacity = parseFloat(document.getElementById('arrowOpacity').value);
const paintOptions = {
'fill-color': fillColor,
'fill-outline-color': outlineColor,
'fill-opacity': opacity
};
arrowParamsMap.set(id, {
splineStep,
offsetDistance,
calculation,
range,
minValue,
widthArrow,
lengthArrow,
paintOptions
});
const arrowGeoJSON = getArrowPolygon({
points: coords,
splineStep,
offsetDistance
}, {
calculation,
range,
minValue
}, {
widthArrow,
lengthArrow
});
//ARROW
const arrowLayerId = ARROW + "-" + id;
if (map.getLayer(arrowLayerId)) map.removeLayer(arrowLayerId);
if (map.getSource(arrowLayerId)) map.removeSource(arrowLayerId);
drawOnMap(arrowGeoJSON, ARROW + "-" + id, paintOptions, map);
const allFeatures = draw.getAll().features;
setCurrentFeature(allFeatures.find(f => f.id === id));
});
document.getElementById('applyFrontlineChanges').addEventListener('click', () => {
if (!currentFeature) return;
const coords = currentFeature.geometry.coordinates;
const id = currentFeature.id;
const splineStep = parseFloat(document.getElementById('splineStepFrontline').value);
const offsetDistance = parseFloat(document.getElementById('offsetDistanceFrontline').value);
const style = parseInt(document.getElementById('styleFrontline').value);
const length = parseFloat(document.getElementById('protrusionLength').value);
const startSize = parseFloat(document.getElementById('protrusionStartSize').value);
const endSize = parseFloat(document.getElementById('protrusionEndSize').value);
const gap = parseFloat(document.getElementById('protrusionGap').value);
const fillColorLeft = document.getElementById('frontlineFillColorLeft').value;
const outlineColorLeft = document.getElementById('frontlineOutlineColorLeft').value;
const opacityLeft = parseFloat(document.getElementById('frontlineOpacityLeft').value);
const paintOptionsLeft = {
'fill-color': fillColorLeft,
'fill-outline-color': outlineColorLeft,
'fill-opacity': opacityLeft
};
const fillColorRight = document.getElementById('frontlineFillColorRight').value;
const outlineColorRight = document.getElementById('frontlineOutlineColorRight').value;
const opacityRight = parseFloat(document.getElementById('frontlineOpacityRight').value);
const paintOptionsRight = {
'fill-color': fillColorRight,
'fill-outline-color': outlineColorRight,
'fill-opacity': opacityRight
};
frontlineParamsMap.set(id, {
splineStep,
offsetDistance,
style,
protrusion: {
length,
startSize,
endSize,
gap
},
paintOptionsLeft,
paintOptionsRight
});
const frontlineData = {
points: coords,
splineStep,
offsetDistance,
style
};
const protrusionData = frontlineParamsMap.get(id).protrusion;
const frontlineGeoJSON = getFrontline(frontlineData, protrusionData);
let polygonToDraw = frontlineGeoJSON;
if (frontlineGeoJSON.leftPoly || frontlineGeoJSON.rightPoly) {
polygonToDraw = frontlineGeoJSON.leftPoly || frontlineGeoJSON.rightPoly;
}
if(frontlineGeoJSON.leftPoly && frontlineGeoJSON.rightPoly){
let polygonToDrawLeft = frontlineGeoJSON.leftPoly;
let polygonToDrawRight = frontlineGeoJSON.rightPoly;
const frontlineLayerId = FRONTLINE + "-" + id;
if (map.getLayer(frontlineLayerId)) map.removeLayer(frontlineLayerId);
if (map.getSource(frontlineLayerId)) map.removeSource(frontlineLayerId);
drawOnMap(polygonToDrawLeft, FRONTLINE + "-" + id + ADDITIONAL_SIDE, paintOptionsLeft, map);
drawOnMap(polygonToDrawRight, FRONTLINE + "-" + (id), paintOptionsRight, map);
const allFeatures = draw.getAll().features;
setCurrentFeature(allFeatures.find(f => f.id === id));
return;
}
const frontlineLayerId = FRONTLINE + "-" + id;
if (map.getLayer(frontlineLayerId + ADDITIONAL_SIDE)) map.removeLayer(frontlineLayerId + ADDITIONAL_SIDE);
if (map.getSource(frontlineLayerId + ADDITIONAL_SIDE)) map.removeSource(frontlineLayerId + ADDITIONAL_SIDE);
if (map.getLayer(frontlineLayerId)) map.removeLayer(frontlineLayerId);
if (map.getSource(frontlineLayerId)) map.removeSource(frontlineLayerId);
let paintOptions;
if(frontlineData.style == LEFT_SIDE){
paintOptions = paintOptionsLeft;
}else if(frontlineData.style == RIGHT_SIDE)
{
paintOptions = paintOptionsRight;
}
drawOnMap(polygonToDraw, FRONTLINE + "-" + id, paintOptions, map);
const allFeatures = draw.getAll().features;
setCurrentFeature(allFeatures.find(f => f.id === id));
});
const buttonsContainer = document.getElementById('drawStyleButtons');
buttonsContainer.addEventListener('click', (event) => {
if (event.target.tagName !== 'BUTTON') return;
Array.from(buttonsContainer.querySelectorAll('button')).forEach(btn => btn.classList.remove('active'));
event.target.classList.add('active');
currentDrawStyle = event.target.getAttribute('data-style');
updateButtonStyles(currentDrawStyle);
});
updateButtonStyles(currentDrawStyle);
});