diff --git a/Drawing.js b/Drawing.js new file mode 100644 index 0000000..7d129b0 --- /dev/null +++ b/Drawing.js @@ -0,0 +1,470 @@ +import { getArrowPolygon } from "athena-utils/shape/Arrow.js"; +import { ARROW_BODY_STYLE_CONSTANT, ARROW_BODY_STYLE_LINEAR, ARROW_BODY_STYLE_EXPONENTIAL } from "athena-utils/shape/Arrow.js"; +import { getFrontline } from "athena-utils/shape/Frontline.js"; +import { LEFT_SIDE, RIGHT_SIDE, BOTH_SIDES } from "athena-utils/shape/Frontline.js"; + +let mouseMoveListener = null; +let currentFeature = null; +let currentDrawStyle = 'arrow'; + +const arrowParamsMap = new Map(); +const frontlineParamsMap = new Map(); + +mapboxgl.accessToken = 'pk.eyJ1Ijoib3V0ZG9vcm1hcHBpbmdjb21wYW55IiwiYSI6ImNqYmh3cDdjYzNsMnozNGxsYzlvMmk2bTYifQ.QqcZ4LVoLWnXafXdjZxnZg'; + +const map = new mapboxgl.Map({ + container: 'map', + style: 'mapbox://styles/mapbox/streets-v12', + center: [10, 50], + zoom: 5 +}); + +const draw = new MapboxDraw({ + displayControlsDefault: false, + controls: { + line_string: true, + trash: true + }, + defaultMode: 'draw_line_string', + styles: [ + // Aktivní linka - úplně průhledná + { + id: 'gl-draw-line', + type: 'line', + filter: ['all', ['==', '$type', 'LineString'], ['==', 'active', 'true']], + layout: { + 'line-cap': 'round', + 'line-join': 'round' + }, + paint: { + 'line-color': '#00ff00', // zelená + 'line-width': 4 + } + }, + // Neaktivní linka - taky průhledná + { + id: 'gl-draw-line-inactive', + type: 'line', + filter: ['all', ['==', '$type', 'LineString'], ['==', 'active', 'false']], + layout: { + 'line-cap': 'round', + 'line-join': 'round' + }, + paint: { + 'line-color': 'rgba(0,0,0,0)', + 'line-width': 2 + } + }, + // Body (vertexy) - průhledné, pokud chceš + { + id: 'gl-draw-polygon-and-line-vertex-active', + type: 'circle', + filter: ['all', ['==', '$type', 'Point'], ['==', 'meta', 'vertex'], ['==', 'active', 'true']], + paint: { + 'circle-radius': 6, + 'circle-color': '#00ff00' // zelená + } + }, + { + id: 'gl-draw-polygon-and-line-vertex-inactive', + type: 'circle', + filter: ['all', ['==', '$type', 'Point'], ['==', 'meta', 'vertex'], ['==', 'active', 'false']], + paint: { + 'circle-radius': 4, + 'circle-color': '#00aa00' // tmavší zelená + } + }, + + // midpointy - musí být viditelné, aby fungovalo kliknutí a přidávání vertexů + { + id: 'gl-draw-polygon-midpoint', + type: 'circle', + filter: ['all', ['==', '$type', 'Point'], ['==', 'meta', 'midpoint']], + paint: { 'circle-radius': 4, 'circle-color': '#fbb03b' } // žlutá + }, + { + id: 'gl-draw-polygon-midpoint-active', + type: 'circle', + filter: ['all', ['==', '$type', 'Point'], ['==', 'meta', 'midpoint'], ['==', 'active', 'true']], + paint: { 'circle-radius': 6, 'circle-color': '#fbb03b' } + } + ] +}); + + + +map.addControl(draw, 'top-left'); +map.on('draw.create', handleDraw); +map.on('draw.delete', () => { + draw.deleteAll(); + clearAllArrowLayers(); + clearAllFrontlineLayers(); + hideArrowEditor(); + hideFrontlineEditor(); + document.getElementById('calculated-area').innerHTML = ''; +}); +map.on('draw.update', handleDraw); +map.on('click', (e) => { + const arrowFeatures = map.queryRenderedFeatures(e.point, { + layers: map.getStyle().layers + .filter(l => l.id.startsWith('arrow-')) + .map(l => l.id) + }); + if (arrowFeatures.length > 0) { + const arrowFeature = arrowFeatures[0]; + const featureId = arrowFeature.layer.id.replace('arrow-', ''); + + const allFeatures = draw.getAll().features; + currentFeature = allFeatures.find(f => f.id === featureId); + + if (currentFeature) { + const params = arrowParamsMap.get(featureId); + if (params) { + showArrowEditor(params); + hideFrontlineEditor(); + } + + draw.changeMode('direct_select', { featureId: featureId }); + } + return; + } + + // Pokud to není arrow, zkus frontline + const frontlineFeatures = map.queryRenderedFeatures(e.point, { + layers: map.getStyle().layers + .filter(l => l.id.startsWith('frontline-')) + .map(l => l.id) + }); + + if (frontlineFeatures.length > 0) { + const frontlineFeature = frontlineFeatures[0]; + const featureId = frontlineFeature.layer.id.replace('frontline-', ''); + + const allFeatures = draw.getAll().features; + currentFeature = allFeatures.find(f => f.id === featureId); + + if (currentFeature) { + const params = frontlineParamsMap.get(featureId); + if (params) { + showFrontlineEditor(params); + hideArrowEditor(); + } + + draw.changeMode('direct_select', { featureId: featureId }); + } + return; + } + + hideArrowEditor(); + hideFrontlineEditor(); +}); +map.dragPan.enable(); + + +function handleDraw(e) { + const data = draw.getAll(); + const answer = document.getElementById('calculated-area'); + + clearAllArrowLayers(); + clearAllFrontlineLayers(); + + if (data.features.length > 0) { + answer.innerHTML = ''; + + data.features.forEach((line, index) => { + if (line.geometry.type !== 'LineString') return; + + const coords = line.geometry.coordinates; + const id = line.id || `line-${index}`; + + if (currentDrawStyle === 'arrow') { + if (!arrowParamsMap.has(id)) { + arrowParamsMap.set(id, { + splineStep: 20, + offsetDistance: 12000, + calculation: ARROW_BODY_STYLE_LINEAR, + range: 1, + minValue: 0.1, + widthArrow: 5, + lengthArrow: 8 + }); + } + + const params = arrowParamsMap.get(id); + + const arrowGeoJSON = getArrowPolygon( + { points: coords, splineStep: params.splineStep, offsetDistance: params.offsetDistance }, + { calculation: params.calculation, range: params.range, minValue: params.minValue }, + { widthArrow: params.widthArrow, lengthArrow: params.lengthArrow } + ); + + drawArrowOnMap(arrowGeoJSON, `arrow-${id}`); + + } else if (currentDrawStyle === 'frontline') { + if (!frontlineParamsMap.has(id)) { + frontlineParamsMap.set(id, { + splineStep: 20, + spacing: 500, + offsetDistance: 12000, + style: LEFT_SIDE, + protrusion: { + length: 2000, + startSize: 5000, + endSize: 5000, + gap: 10000 + } + }); + } + + const params = frontlineParamsMap.get(id); + + const frontlineData = { + points: coords, + splineStep: params.splineStep, + spacing: params.spacing, + offsetDistance: params.offsetDistance, + style: params.style + }; + + const protrusionData = params.protrusion; + + const frontlineGeoJSON = getFrontline(frontlineData, protrusionData); + + let polygonToDraw = frontlineGeoJSON; + if (frontlineGeoJSON.leftPoly || frontlineGeoJSON.rightPoly) { + polygonToDraw = frontlineGeoJSON.leftPoly || frontlineGeoJSON.rightPoly; + } + + drawFrontlineOnMap(polygonToDraw, `frontline-${id}`); + } + }); + } else { + answer.innerHTML = ''; + if (e.type !== 'draw.delete') + alert('Klikni na mapu pro kreslení čáry.'); + } +} + +function drawArrowOnMap(geojson, id) { + + if (map.getLayer(id)) map.removeLayer(id); + if (map.getSource(id)) map.removeSource(id); + + map.addSource(id, { + type: 'geojson', + data: geojson + }); + + map.addLayer({ + id: id, + type: 'fill', + source: id, + paint: { + 'fill-color': '#ff0000', + 'fill-opacity': 0.5 + } + }); + console.log('HERE'); +} + +function drawFrontlineOnMap(frontlineGeoJSON, id) { + if (map.getLayer(id)) map.removeLayer(id); + if (map.getSource(id)) map.removeSource(id); + + map.addSource(id, { + type: 'geojson', + data: frontlineGeoJSON + }); + + map.addLayer({ + id: id, + type: 'fill', + source: id, + paint: { + 'fill-color': '#ff6600', + 'fill-opacity': 0.6, + 'fill-outline-color': '#cc3300' + } + }); +} + +function clearAllArrowLayers() { + const layers = map.getStyle().layers; + if (!layers) return; + + layers.forEach(layer => { + if (layer.id.startsWith('arrow-')) { + if (map.getLayer(layer.id)) map.removeLayer(layer.id); + if (map.getSource(layer.id)) map.removeSource(layer.id); + } + }); +} + +function clearAllFrontlineLayers() { + const layers = map.getStyle().layers; + if (!layers) return; + + layers.forEach(layer => { + if (layer.id.startsWith('frontline-')) { + if (map.getLayer(layer.id)) map.removeLayer(layer.id); + if (map.getSource(layer.id)) map.removeSource(layer.id); + } + }); +} + +function showArrowEditor(params) { + const popup = document.getElementById('arrow-editor'); + popup.style.display = 'block'; + + popup.style.left = '10px'; + popup.style.top = '70px'; + + document.getElementById('splineStep').value = params.splineStep; + document.getElementById('offsetDistance').value = params.offsetDistance; + document.getElementById('range').value = params.range; + document.getElementById('minValue').value = params.minValue; + document.getElementById('widthArrow').value = params.widthArrow; + document.getElementById('lengthArrow').value = params.lengthArrow; +} + + + +function hideArrowEditor() { + const popup = document.getElementById('arrow-editor'); + popup.style.display = 'none'; + currentFeature = null; +} + +function showFrontlineEditor(params) { + const popup = document.getElementById('frontline-editor'); + popup.style.display = 'block'; + + popup.style.left = '10px'; + popup.style.top = '70px'; + + document.getElementById('splineStepFrontline').value = params.splineStep; + document.getElementById('spacing').value = params.spacing; + document.getElementById('offsetDistanceFrontline').value = params.offsetDistance; + document.getElementById('styleFrontline').value = params.style; + + document.getElementById('protrusionLength').value = params.protrusion.length; + document.getElementById('protrusionStartSize').value = params.protrusion.startSize; + document.getElementById('protrusionEndSize').value = params.protrusion.endSize; + document.getElementById('protrusionGap').value = params.protrusion.gap; +} + +function hideFrontlineEditor() { + const popup = document.getElementById('frontline-editor'); + popup.style.display = 'none'; + currentFeature = null; +} + +document.addEventListener('DOMContentLoaded', () => { +document.getElementById('applyArrowChanges').addEventListener('click', () => { + + console.log("arrowFeatures"); + console.log(arrowFeatures); + if (!currentFeature) return; + console.log("Test"); + 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); + + arrowParamsMap.set(id, { + splineStep, + offsetDistance, + calculation: ARROW_BODY_STYLE_LINEAR, + range, + minValue, + widthArrow, + lengthArrow + }); + + const arrowGeoJSON = getArrowPolygon({ + points: coords, + splineStep, + offsetDistance + }, { + calculation: ARROW_BODY_STYLE_LINEAR, + range, + minValue + }, { + widthArrow, + lengthArrow + }); + + drawArrowOnMap(arrowGeoJSON, `arrow-${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 spacing = parseFloat(document.getElementById('spacing').value); + const offsetDistance = parseFloat(document.getElementById('offsetDistanceFrontline').value); + const style = 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); + + frontlineParamsMap.set(id, { + splineStep, + spacing, + offsetDistance, + style, + protrusion: { + length, + startSize, + endSize, + gap + } + }); + + const frontlineData = { + points: coords, + splineStep, + spacing, + 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; + } + + drawFrontlineOnMap(polygonToDraw, `frontline-${id}`); +}); + +const buttonsContainer = document.getElementById('drawStyleButtons'); +buttonsContainer.addEventListener('click', (event) => { + if (event.target.tagName !== 'BUTTON') return; + + // Odeber aktivní třídu ze všech tlačítek + Array.from(buttonsContainer.querySelectorAll('button')).forEach(btn => btn.classList.remove('active')); + + // Nastav aktivní třídu na kliknuté tlačítko + event.target.classList.add('active'); + + // Nastav styl podle data atributu + currentDrawStyle = event.target.getAttribute('data-style'); + + // Při změně stylu překreslíme aktuální kreslené prvky + handleDraw({ type: 'draw.update' }); +}); +}); \ No newline at end of file diff --git a/MapPolygons.js b/MapPolygons.js index 37d53ea..6662672 100644 --- a/MapPolygons.js +++ b/MapPolygons.js @@ -12,7 +12,8 @@ const map = new mapboxgl.Map({ center: [10, 50], zoom: 5 }); - +//this is my test push +//second map.on('load', () => { const fullPolygon = getArrowPolygon(arrowData, style, arrowHeadData); const circleGeoJSON = getCirclePolygon(circleCenter, circleRadius, circleDensity); diff --git a/index.html b/index.html index 3c3b183..dfe9928 100644 --- a/index.html +++ b/index.html @@ -1,6 +1,117 @@ + + + + + + Draw a polygon and calculate its area + + + + + + + + + + + + +
+ + + + + + +
+ + + +
+ + +
+

Click the map to draw a polygon.

+
+
+ + + + + + + @@ -19,7 +130,7 @@ body { margin: 0; padding: 0; } - +-->