Fix KMZ overlays and add map export
This commit is contained in:
1 parent
32a36c98ec
commit
78af860493
8 files changed
+495
-29
No files matched your search
+155
-21
@@ -61,6 +61,7 @@ window.lmeMap = {
|
||||
startMeasurementDrawing,
|
||||
setDrawToolsVisible,
|
||||
clearDrawings,
|
||||
createMapExportGeoJson,
|
||||
createProjectSnapshot,
|
||||
loadProjectSnapshot,
|
||||
getLegacyProjectSnapshot,
|
||||
@@ -752,10 +753,10 @@ function kmlTextToGeoJson(text, sourceName) {
|
||||
|
||||
function filterKmlParcelFeatures(features) {
|
||||
const filtered = features.filter(feature => {
|
||||
const properties = feature.properties || {};
|
||||
if (properties["@geometry-type"] === "groundoverlay") {
|
||||
if (isSheetOrOverlayFeature(feature)) {
|
||||
return false;
|
||||
}
|
||||
const properties = feature.properties || {};
|
||||
const name = String(properties.name || properties.Name || properties.LABEL || "").trim();
|
||||
const identity = [name, properties.layer, properties.Layer, properties.type, properties.Tipo]
|
||||
.filter(Boolean)
|
||||
@@ -770,6 +771,28 @@ function filterKmlParcelFeatures(features) {
|
||||
return removeLikelySheetOutlines(filtered);
|
||||
}
|
||||
|
||||
function isSheetOrOverlayFeature(feature) {
|
||||
const properties = feature?.properties || {};
|
||||
const geometryType = String(properties["@geometry-type"] || "").toLowerCase();
|
||||
const name = String(properties.name || properties.Name || properties.LABEL || "").trim();
|
||||
const context = [
|
||||
name,
|
||||
properties.layer,
|
||||
properties.Layer,
|
||||
properties.type,
|
||||
properties.Tipo,
|
||||
properties._lmeFolder
|
||||
].filter(Boolean).join(" ").toLowerCase();
|
||||
|
||||
if (geometryType === "groundoverlay" || /^doc[_ -]?\d+$/i.test(name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const hasParcelNumber = /^[a-z]?\s*\d+[a-z]?(?:[\/. -]\d+[a-z]?)?$/i.test(name);
|
||||
return !hasParcelNumber
|
||||
&& /\bfoglio\b|\bsheet\b|quadro[ _-]?d[' ]?unione|(?:perimetro|bordo|limite|contorno)[ _-]?(?:del[ _-]?)?foglio|sheet[ _-]?boundary/.test(context);
|
||||
}
|
||||
|
||||
function splitKmlPolygonFeatures(features) {
|
||||
const result = [];
|
||||
const addGeometry = (feature, geometry, partIndex) => {
|
||||
@@ -876,6 +899,7 @@ function setIncludeKmlSheetLayer(enabled) {
|
||||
}
|
||||
|
||||
function setImportedSheetLayerVisible(enabled) {
|
||||
migrateMisclassifiedImportedSheets();
|
||||
lmeState.importedSheetsVisible = Boolean(enabled);
|
||||
applyImportedSheetVisibility();
|
||||
const count = lmeState.importedSheets?.toGeoJSON()?.features?.length || 0;
|
||||
@@ -884,6 +908,50 @@ function setImportedSheetLayerVisible(enabled) {
|
||||
: "Layer del foglio importato nascosto; le particelle restano visibili.");
|
||||
}
|
||||
|
||||
function migrateMisclassifiedImportedSheets() {
|
||||
if (!lmeState.imported || !lmeState.importedSheets) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const current = lmeState.imported.toGeoJSON();
|
||||
const misplaced = (current.features || []).filter(isSheetOrOverlayFeature);
|
||||
if (!misplaced.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const retained = (current.features || []).filter(feature => !isSheetOrOverlayFeature(feature));
|
||||
lmeState.imported.clearLayers();
|
||||
addImportedFeatures({ type: "FeatureCollection", features: retained });
|
||||
addImportedSheetFeatures({ type: "FeatureCollection", features: misplaced });
|
||||
refreshImportedMeasurements();
|
||||
return misplaced.length;
|
||||
}
|
||||
|
||||
function addImportedFeatures(geojson) {
|
||||
return L.geoJSON(geojson || emptyFeatureCollection(), {
|
||||
style: { color: "#22c55e", weight: 3, opacity: 0.95, fillOpacity: 0.18 },
|
||||
pointToLayer: (feature, latlng) => L.circleMarker(latlng, {
|
||||
radius: 7,
|
||||
color: "#22c55e",
|
||||
fillColor: "#ffffff",
|
||||
fillOpacity: 0.9
|
||||
}),
|
||||
onEachFeature: bindStoredFeaturePopup
|
||||
}).addTo(lmeState.imported);
|
||||
}
|
||||
|
||||
function addImportedSheetFeatures(geojson) {
|
||||
return L.geoJSON(geojson || emptyFeatureCollection(), {
|
||||
style: { color: "#15803d", weight: 3, opacity: 0.85, fillOpacity: 0.04, dashArray: "9 7" },
|
||||
pointToLayer: (feature, latlng) => L.circleMarker(latlng, {
|
||||
radius: 6,
|
||||
color: "#15803d",
|
||||
fillOpacity: 0.25
|
||||
}),
|
||||
onEachFeature: bindStoredFeaturePopup
|
||||
}).addTo(lmeState.importedSheets);
|
||||
}
|
||||
|
||||
function applyImportedSheetVisibility() {
|
||||
if (!lmeState.map || !lmeState.importedSheets) {
|
||||
return;
|
||||
@@ -1355,6 +1423,85 @@ function formatNumber(value, digits) {
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
function createMapExportGeoJson(options) {
|
||||
migrateMisclassifiedImportedSheets();
|
||||
const requested = options || {};
|
||||
const features = [];
|
||||
|
||||
if (requested.includeImported !== false) {
|
||||
appendExportFeatures(features, lmeState.imported?.toGeoJSON(), "imported");
|
||||
}
|
||||
if (requested.includeImportedSheets !== false) {
|
||||
appendExportFeatures(features, lmeState.importedSheets?.toGeoJSON(), "imported-sheet");
|
||||
}
|
||||
if (requested.includeCadastral !== false) {
|
||||
appendExportFeatures(features, lmeState.selectedParcelsLayer?.toGeoJSON(), "cadastral");
|
||||
appendExportFeatures(features, lmeState.gpsParcelLayer?.toGeoJSON(), "cadastral-gps");
|
||||
}
|
||||
|
||||
const drawings = lmeState.drawings?.toGeoJSON() || emptyFeatureCollection();
|
||||
if (requested.includeDrawings !== false) {
|
||||
appendExportFeatures(features, drawings, "drawing", feature => !isPoiFeature(feature));
|
||||
}
|
||||
if (requested.includePoi !== false) {
|
||||
appendExportFeatures(features, drawings, "poi", isPoiFeature);
|
||||
}
|
||||
if (requested.includeTrail !== false && lmeState.gpsTrailPoints.length >= 2) {
|
||||
features.push({
|
||||
type: "Feature",
|
||||
properties: { name: "Traccia GPS", _lmeCategory: "gps-trail" },
|
||||
geometry: {
|
||||
type: "LineString",
|
||||
coordinates: lmeState.gpsTrailPoints.map(point => [
|
||||
Number(point.lng ?? point[1]),
|
||||
Number(point.lat ?? point[0])
|
||||
])
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return JSON.stringify({ type: "FeatureCollection", features });
|
||||
}
|
||||
|
||||
function appendExportFeatures(target, collection, category, predicate = () => true) {
|
||||
for (const sourceFeature of collection?.features || []) {
|
||||
if (!sourceFeature?.geometry || !predicate(sourceFeature)) {
|
||||
continue;
|
||||
}
|
||||
const feature = JSON.parse(JSON.stringify(sourceFeature));
|
||||
feature.properties = { ...(feature.properties || {}), _lmeCategory: category };
|
||||
if (feature.properties._lmeShape === "circle"
|
||||
&& feature.geometry.type === "Point"
|
||||
&& Number(feature.properties._lmeRadius) > 0) {
|
||||
feature.geometry = circleToPolygon(feature.geometry.coordinates, Number(feature.properties._lmeRadius));
|
||||
}
|
||||
target.push(feature);
|
||||
}
|
||||
}
|
||||
|
||||
function isPoiFeature(feature) {
|
||||
return feature?.geometry?.type === "Point"
|
||||
&& feature?.properties?._lmeShape !== "circle";
|
||||
}
|
||||
|
||||
function circleToPolygon(center, radiusMeters) {
|
||||
const longitude = Number(center?.[0]);
|
||||
const latitude = Number(center?.[1]);
|
||||
const earthRadius = 6378137;
|
||||
const latitudeRadians = latitude * Math.PI / 180;
|
||||
const ring = [];
|
||||
for (let index = 0; index <= 64; index += 1) {
|
||||
const angle = index / 64 * Math.PI * 2;
|
||||
const north = Math.sin(angle) * radiusMeters;
|
||||
const east = Math.cos(angle) * radiusMeters;
|
||||
ring.push([
|
||||
longitude + east / (earthRadius * Math.cos(latitudeRadians)) * 180 / Math.PI,
|
||||
latitude + north / earthRadius * 180 / Math.PI
|
||||
]);
|
||||
}
|
||||
return { type: "Polygon", coordinates: [ring] };
|
||||
}
|
||||
|
||||
function createProjectSnapshot(includeCadastralParcels) {
|
||||
const center = lmeState.map.getCenter();
|
||||
return JSON.stringify({
|
||||
@@ -1423,25 +1570,8 @@ function loadProjectSnapshot(raw, projectName) {
|
||||
const importedParts = partitionStoredImportedFeatures(
|
||||
data.imported || emptyFeatureCollection(),
|
||||
data.importedSheets || emptyFeatureCollection());
|
||||
L.geoJSON(importedParts.parcels, {
|
||||
style: { color: "#22c55e", weight: 3, opacity: 0.95, fillOpacity: 0.18 },
|
||||
pointToLayer: (feature, latlng) => L.circleMarker(latlng, {
|
||||
radius: 7,
|
||||
color: "#22c55e",
|
||||
fillColor: "#ffffff",
|
||||
fillOpacity: 0.9
|
||||
}),
|
||||
onEachFeature: bindStoredFeaturePopup
|
||||
}).addTo(lmeState.imported);
|
||||
L.geoJSON(importedParts.sheets, {
|
||||
style: { color: "#15803d", weight: 3, opacity: 0.85, fillOpacity: 0.04, dashArray: "9 7" },
|
||||
pointToLayer: (feature, latlng) => L.circleMarker(latlng, {
|
||||
radius: 6,
|
||||
color: "#15803d",
|
||||
fillOpacity: 0.25
|
||||
}),
|
||||
onEachFeature: bindStoredFeaturePopup
|
||||
}).addTo(lmeState.importedSheets);
|
||||
addImportedFeatures(importedParts.parcels);
|
||||
addImportedSheetFeatures(importedParts.sheets);
|
||||
const projectDisplayOptions = data.schemaVersion >= 3 && data.displayOptions
|
||||
? data.displayOptions
|
||||
: null;
|
||||
@@ -1557,6 +1687,10 @@ function partitionStoredImportedFeatures(imported, storedSheets) {
|
||||
const sourceGroups = new Map();
|
||||
|
||||
(imported?.features || []).forEach(feature => {
|
||||
if (isSheetOrOverlayFeature(feature)) {
|
||||
sheetFeatures.push(feature);
|
||||
return;
|
||||
}
|
||||
const source = String(feature?.properties?._lmeSource || "");
|
||||
if (!/\.(kml|kmz)$/i.test(source)) {
|
||||
parcelFeatures.push(feature);
|
||||
|
||||
Reference in new issue
Block a user