Reorganize map tools and improve KML sheet filtering
This commit is contained in:
1 parent
c8b8f21d8d
commit
7933b2e86a
6 files changed
+331
-173
No files matched your search
+81
-47
@@ -13,7 +13,7 @@ const lmeState = {
|
||||
drawControl: null,
|
||||
activeDrawHandler: null,
|
||||
drawEventsBound: false,
|
||||
importParcelOnly: true,
|
||||
includeKmlSheetLayer: false,
|
||||
showImportedMeasurements: false,
|
||||
showDrawingMeasurements: true,
|
||||
measurementRefreshTimer: null,
|
||||
@@ -47,7 +47,7 @@ window.lmeMap = {
|
||||
clearMapElements,
|
||||
fitImported,
|
||||
clearImported,
|
||||
setImportParcelOnly,
|
||||
setIncludeKmlSheetLayer,
|
||||
setImportedMeasurementsVisible,
|
||||
setDrawingMeasurementsVisible,
|
||||
addProjectMarker,
|
||||
@@ -447,7 +447,7 @@ function bindFileImport() {
|
||||
refreshImportedMeasurements();
|
||||
const featureCount = geojson.features.length;
|
||||
const ignored = result.ignoredCount
|
||||
? `; esclusi ${result.ignoredCount} elementi non particella`
|
||||
? `; esclusi ${result.ignoredCount} elementi del foglio o non particella`
|
||||
: "";
|
||||
const reprojected = result.reprojected ? "; coordinate Web Mercator convertite" : "";
|
||||
setInfo(`Caricato ${file.name}: ${featureCount} elementi${ignored}${reprojected}.`);
|
||||
@@ -652,13 +652,14 @@ function normalizeKmlFiles(files) {
|
||||
throw new Error("KML senza geometrie utilizzabili");
|
||||
}
|
||||
|
||||
const selected = lmeState.importParcelOnly ? filterKmlParcelFeatures(features) : features;
|
||||
const polygonParts = splitKmlPolygonFeatures(features);
|
||||
const selected = lmeState.includeKmlSheetLayer ? features : filterKmlParcelFeatures(polygonParts);
|
||||
if (!selected.length) {
|
||||
throw new Error("nessuna particella trovata; disattiva 'solo particelle' per importare tutti gli elementi");
|
||||
throw new Error("nessuna particella trovata; attiva 'includi layer del foglio' per importare tutti gli elementi");
|
||||
}
|
||||
return {
|
||||
geojson: { type: "FeatureCollection", features: selected },
|
||||
ignoredCount: features.length - selected.length,
|
||||
ignoredCount: lmeState.includeKmlSheetLayer ? 0 : Math.max(0, polygonParts.length - selected.length),
|
||||
reprojected: false
|
||||
};
|
||||
}
|
||||
@@ -690,10 +691,7 @@ function kmlTextToGeoJson(text, sourceName) {
|
||||
}
|
||||
|
||||
function filterKmlParcelFeatures(features) {
|
||||
const polygons = features
|
||||
.map(feature => ({ ...feature, geometry: polygonOnlyGeometry(feature.geometry) }))
|
||||
.filter(feature => feature.geometry);
|
||||
const filtered = polygons.filter(feature => {
|
||||
const filtered = features.filter(feature => {
|
||||
const properties = feature.properties || {};
|
||||
if (properties["@geometry-type"] === "groundoverlay") {
|
||||
return false;
|
||||
@@ -706,45 +704,81 @@ function filterKmlParcelFeatures(features) {
|
||||
const folder = String(properties._lmeFolder || "").toLowerCase();
|
||||
const isParcel = /particell|mappal|parcel/.test(`${identity} ${folder}`)
|
||||
|| /^[a-z]?\s*\d+[a-z]?(?:[\/. -]\d+[a-z]?)?$/i.test(name);
|
||||
const isSheet = /\bfoglio\b|\bsheet\b|quadro[ _-]?d[' ]?unione|perimetro[ _-]?foglio/.test(identity);
|
||||
const isSheet = /\bfoglio\b|\bsheet\b|quadro[ _-]?d[' ]?unione|(?:perimetro|bordo|limite|contorno)[ _-]?(?:del[ _-]?)?foglio|sheet[ _-]?boundary/.test(identity);
|
||||
return isParcel || !isSheet;
|
||||
});
|
||||
return removeLikelySheetOutline(filtered);
|
||||
return removeLikelySheetOutlines(filtered);
|
||||
}
|
||||
|
||||
function polygonOnlyGeometry(geometry) {
|
||||
if (!geometry) {
|
||||
return null;
|
||||
}
|
||||
if (geometry.type === "Polygon" || geometry.type === "MultiPolygon") {
|
||||
return geometry;
|
||||
}
|
||||
if (geometry.type === "GeometryCollection") {
|
||||
const geometries = (geometry.geometries || []).filter(item =>
|
||||
item.type === "Polygon" || item.type === "MultiPolygon");
|
||||
return geometries.length ? { type: "GeometryCollection", geometries } : null;
|
||||
}
|
||||
return null;
|
||||
function splitKmlPolygonFeatures(features) {
|
||||
const result = [];
|
||||
const addGeometry = (feature, geometry, partIndex) => {
|
||||
if (!geometry) {
|
||||
return;
|
||||
}
|
||||
if (geometry.type === "Polygon") {
|
||||
result.push({
|
||||
...feature,
|
||||
properties: { ...(feature.properties || {}), _lmePart: partIndex },
|
||||
geometry
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (geometry.type === "MultiPolygon") {
|
||||
(geometry.coordinates || []).forEach((coordinates, index) => addGeometry(
|
||||
feature,
|
||||
{ type: "Polygon", coordinates },
|
||||
`${partIndex}.${index}`));
|
||||
return;
|
||||
}
|
||||
if (geometry.type === "GeometryCollection") {
|
||||
(geometry.geometries || []).forEach((item, index) => addGeometry(feature, item, `${partIndex}.${index}`));
|
||||
}
|
||||
};
|
||||
features.forEach((feature, index) => addGeometry(feature, feature.geometry, index));
|
||||
return result;
|
||||
}
|
||||
|
||||
function removeLikelySheetOutline(features) {
|
||||
if (features.length < 3) {
|
||||
return features;
|
||||
function removeLikelySheetOutlines(features) {
|
||||
let remaining = [...features];
|
||||
while (remaining.length >= 2) {
|
||||
const candidates = remaining
|
||||
.map(feature => ({ feature, bounds: geoJsonBounds(feature.geometry) }))
|
||||
.filter(item => item.bounds);
|
||||
if (candidates.length < 2) {
|
||||
break;
|
||||
}
|
||||
|
||||
const largest = candidates.reduce((current, item) =>
|
||||
boundsArea(item.bounds) > boundsArea(current.bounds) ? item : current);
|
||||
if (isStrongParcelFeature(largest.feature)) {
|
||||
break;
|
||||
}
|
||||
const others = candidates.filter(item => item !== largest);
|
||||
const comparisonAreas = others.map(item => boundsArea(item.bounds)).sort((a, b) => a - b);
|
||||
const referenceArea = comparisonAreas[Math.floor(comparisonAreas.length / 2)];
|
||||
const contained = others.filter(item => boundsContains(largest.bounds, boundsCenter(item.bounds))).length;
|
||||
const minimumContained = candidates.length === 2 ? 1 : Math.ceil(others.length * 0.6);
|
||||
const areaFactor = 2.5;
|
||||
const looksLikeSheet = referenceArea > 0
|
||||
&& boundsArea(largest.bounds) > referenceArea * areaFactor
|
||||
&& contained >= minimumContained;
|
||||
if (!looksLikeSheet) {
|
||||
break;
|
||||
}
|
||||
remaining = remaining.filter(feature => feature !== largest.feature);
|
||||
}
|
||||
const candidates = features.map(feature => ({ feature, bounds: geoJsonBounds(feature.geometry) }));
|
||||
const valid = candidates.filter(item => item.bounds);
|
||||
if (valid.length < 3) {
|
||||
return features;
|
||||
}
|
||||
const areas = valid.map(item => boundsArea(item.bounds)).sort((a, b) => a - b);
|
||||
const median = areas[Math.floor(areas.length / 2)];
|
||||
const largest = valid.reduce((current, item) =>
|
||||
boundsArea(item.bounds) > boundsArea(current.bounds) ? item : current);
|
||||
const others = valid.filter(item => item !== largest);
|
||||
const contained = others.filter(item => boundsContains(largest.bounds, boundsCenter(item.bounds))).length;
|
||||
const looksLikeSheet = boundsArea(largest.bounds) > median * 8
|
||||
&& contained >= Math.ceil(others.length * 0.75);
|
||||
return looksLikeSheet ? features.filter(feature => feature !== largest.feature) : features;
|
||||
return remaining;
|
||||
}
|
||||
|
||||
function isStrongParcelFeature(feature) {
|
||||
const properties = feature?.properties || {};
|
||||
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(" ");
|
||||
return /particell|mappal|parcel/i.test(context)
|
||||
|| /^[a-z]?\s*\d+[a-z]?(?:[\/. -]\d+[a-z]?)?$/i.test(name);
|
||||
}
|
||||
|
||||
function geoJsonBounds(geometry) {
|
||||
@@ -774,11 +808,11 @@ function boundsContains(bounds, point) {
|
||||
&& point[1] >= bounds.south && point[1] <= bounds.north;
|
||||
}
|
||||
|
||||
function setImportParcelOnly(enabled) {
|
||||
lmeState.importParcelOnly = Boolean(enabled);
|
||||
setInfo(lmeState.importParcelOnly
|
||||
? "Importazione KML/KMZ: verranno caricate solo le particelle."
|
||||
: "Importazione KML/KMZ: verranno caricati tutti gli elementi.");
|
||||
function setIncludeKmlSheetLayer(enabled) {
|
||||
lmeState.includeKmlSheetLayer = Boolean(enabled);
|
||||
setInfo(lmeState.includeKmlSheetLayer
|
||||
? "Importazione KML/KMZ: verranno inclusi anche foglio e altri elementi."
|
||||
: "Importazione KML/KMZ: verranno caricate solo le particelle riconosciute.");
|
||||
}
|
||||
|
||||
function setImportedMeasurementsVisible(enabled) {
|
||||
|
||||
Reference in new issue
Block a user