@regionerne/gis-komponent 0.0.84 → 0.0.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -61,8 +61,8 @@ import Feature$1 from 'ol/Feature';
61
61
  import { never, always } from 'ol/events/condition';
62
62
  import { buffer, lineIntersect, lineSplit, featureCollection, difference, booleanIntersects, union, point, booleanPointInPolygon, area, flatten, feature, booleanWithin, intersect, bboxPolygon } from '@turf/turf';
63
63
  import GeoJSON$1 from 'ol/format/GeoJSON';
64
+ import { containsXY, extend, createEmpty, buffer as buffer$1 } from 'ol/extent';
64
65
  import { Feature } from 'ol';
65
- import { extend, createEmpty, buffer as buffer$1 } from 'ol/extent';
66
66
  import { easeOut } from 'ol/easing';
67
67
  import Select from 'ol/interaction/Select';
68
68
  import html2canvas from 'html2canvas-pro';
@@ -1591,9 +1591,9 @@ class GeometrySplitService {
1591
1591
  // Move back to 25832
1592
1592
  bufferedLine.geometry.coordinates = bufferedLine.geometry.coordinates.map(a => a.map(c => proj4('EPSG:4326', 'EPSG:25832', c)));
1593
1593
  const overlappingFeatures = vectorSource.getFeatures().filter(f => !this._featureHelper.isLocked(f) && f.getGeometry()?.intersectsExtent(lineFeature.getGeometry()?.getExtent()));
1594
+ const lineFeatureObject = this.geoJsonFormat.writeFeatureObject(lineFeature, { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:25832' });
1594
1595
  overlappingFeatures.filter(f => f.getGeometry()?.getType() === 'LineString').forEach(feature => {
1595
1596
  const linestringFeatureObject = this.geoJsonFormat.writeFeatureObject(feature, { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:25832' });
1596
- const lineFeatureObject = this.geoJsonFormat.writeFeatureObject(lineFeature, { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:25832' });
1597
1597
  if (lineIntersect(lineFeatureObject, linestringFeatureObject)) {
1598
1598
  const lines = lineSplit(linestringFeatureObject, lineFeatureObject);
1599
1599
  const newFeatures = lines.features.map(lineSplitFeature => {
@@ -1607,8 +1607,19 @@ class GeometrySplitService {
1607
1607
  vectorSource.removeFeature(feature);
1608
1608
  }
1609
1609
  });
1610
- // Handle polygons
1611
- overlappingFeatures.filter(f => f.getGeometry()?.getType() === 'Polygon').forEach((feature) => {
1610
+ // Handle polygons - if the split line starts or ends inside of a polygon, that polygon is NOT split!
1611
+ // That split leaves weird lines in the poly and it's handled by excluding those polys alltogether.
1612
+ const splitLineCoordinates = lineFeature.getGeometry().getCoordinates();
1613
+ const coordLength = splitLineCoordinates.length;
1614
+ const lineFirstPoint = splitLineCoordinates[0];
1615
+ const lineLastPoint = splitLineCoordinates[coordLength - 1];
1616
+ overlappingFeatures.filter(f => f.getGeometry()?.getType() === 'Polygon')
1617
+ .filter(feature => {
1618
+ const containsStartingPoint = this.isPointStrictlyInside(feature.getGeometry(), lineFirstPoint);
1619
+ const containsEndPoint = this.isPointStrictlyInside(feature.getGeometry(), lineLastPoint);
1620
+ return !containsStartingPoint && !containsEndPoint;
1621
+ })
1622
+ .forEach((feature) => {
1612
1623
  const overlappingFeatureGeoJson = this.geoJsonFormat.writeFeatureObject(feature);
1613
1624
  const features = featureCollection([overlappingFeatureGeoJson, bufferedLine]);
1614
1625
  const clipped = difference(features);
@@ -1627,6 +1638,15 @@ class GeometrySplitService {
1627
1638
  }
1628
1639
  });
1629
1640
  }
1641
+ isPointStrictlyInside(geom, coord) {
1642
+ if (!geom.intersectsCoordinate(coord))
1643
+ return false;
1644
+ const extent = geom.getExtent();
1645
+ // Hvis punktet ligger udenfor extents → false
1646
+ if (!containsXY(extent, coord[0], coord[1]))
1647
+ return false;
1648
+ return true; // inde og ikke kun på kanten
1649
+ }
1630
1650
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: GeometrySplitService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1631
1651
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: GeometrySplitService, providedIn: 'root' });
1632
1652
  }
@@ -2950,6 +2970,7 @@ class ToolboxComponent {
2950
2970
  _originalMapHeight = 0;
2951
2971
  pointClickKey;
2952
2972
  _geometrySearchService = inject(GeometrySearchService);
2973
+ // private _geomClipService = inject(GeometryClipService);
2953
2974
  drawInteraction;
2954
2975
  get showDocumentSearch() {
2955
2976
  return this.settings?.showDocumentSearch !== false;
@@ -3485,31 +3506,45 @@ class ToolboxComponent {
3485
3506
  });
3486
3507
  this._interactionHelper.setAsTemp(clipHoleDraw);
3487
3508
  clipHoleDraw.on('drawend', evt => {
3488
- const overlappingFeatures = this._drawLayerService.source.getFeatures().filter(f => !this._featureHelper.isLocked(f) && f.getGeometry()?.intersectsExtent(evt.feature.getGeometry().getExtent()));
3489
- const geoJsonFormat = new GeoJSON$1();
3509
+ const featuresInExtent = this._drawLayerService.source.getFeatures().filter(f => !this._featureHelper.isLocked(f) && f.getGeometry()?.intersectsExtent(evt.feature.getGeometry().getExtent()));
3510
+ const formatterOptions = { dataProjection: 'EPSG:4326', featureProjection: 'EPSG:25832' };
3511
+ const geoJsonFormat = new GeoJSON$1(formatterOptions);
3490
3512
  const holeGeoJson = geoJsonFormat.writeFeatureObject(evt.feature);
3491
- overlappingFeatures.forEach(f => {
3492
- const overlappingFeatureGeoJson = geoJsonFormat.writeFeatureObject(f);
3493
- // because the difference function takes featurecollection, I add the two features to a collection
3494
- // I have to cast this as any, because the featurecollection needs to be of Polygon or MultiPoly and it
3495
- // doesnt resolve to that.
3496
- const features = featureCollection([overlappingFeatureGeoJson, holeGeoJson]);
3497
- const clipped = difference(features);
3498
- if (clipped) {
3499
- // Ensures that the clipped feature is split into an array of features. Handles possible MultiPolygon
3500
- const allPolys = flatten(clipped);
3501
- allPolys.features.forEach((poly, idx) => {
3502
- const geom = geoJsonFormat.readGeometry(poly.geometry);
3503
- if (idx === 0) {
3504
- f.setGeometry(geom);
3505
- }
3506
- else {
3507
- const newFeature = f.clone();
3508
- newFeature.setGeometry(geom);
3509
- this._featureHelper.setId(newFeature);
3510
- this._drawLayerService.source.addFeature(newFeature);
3511
- }
3512
- });
3513
+ // Turf is not always able to handle difference if two polygons are completely on top of each other at the edge
3514
+ // That can happen when Snap is active. To handle that, I do a tiny huffer around the hole
3515
+ const bufferedHole = buffer(holeGeoJson, 0.01, { units: 'meters' });
3516
+ const wktFormat = new WKT();
3517
+ // This is a lot of code to handle that Turf is not very precise for larger areas, so I have to handle that. Because the clip will sometimes leave a small snippet
3518
+ // that needs to be removed. Tha mostly happens on large areas and with snap enabled for the draw interaction.
3519
+ const overlappingItems$ = featuresInExtent
3520
+ .map(featureInExtent => ({ feature: featureInExtent, overlappingFeatureGeoJson: geoJsonFormat.writeFeatureObject(featureInExtent) })) // Here, I just build the items I need later
3521
+ .map(item => ({ ...item, threshold: area(item.overlappingFeatureGeoJson) / 10000, clipped: difference(featureCollection([item.overlappingFeatureGeoJson, bufferedHole])) }))
3522
+ .filter(item => !!item.clipped) // Filter out the items that aren't clipped. They are in the same extent, but don't actually overlap, so I leave those out
3523
+ .map(item => ({ ...item, allPolys: flatten(item.clipped) })) // The clipped item can be an array of item, so I make sure to flatten it.
3524
+ .map(item => ({ ...item, allPolysAsWkt: item.allPolys.features.map(poly => wktFormat.writeGeometry(geoJsonFormat.readGeometry(poly.geometry))) }))
3525
+ .map(item => item.allPolysAsWkt.map(wkt => this._geometryService.validate(wkt).pipe(map(result => // Now, I need to validate all the clipped items
3526
+ result.correctedGeometries ? { ...item, validWKTS: result.correctedGeometries } : { ...item, validWKTS: [wkt] } // If there were corrections, I return the corrected items. If not, just the original
3527
+ ))))
3528
+ .flatMap(r => r);
3529
+ combineLatest(overlappingItems$).subscribe({
3530
+ next: validatedItems => {
3531
+ const featuresToAdd = validatedItems
3532
+ .map(item => item.validWKTS
3533
+ .filter(wkt => {
3534
+ const feature = wktFormat.readFeature(wkt);
3535
+ return area(geoJsonFormat.writeFeatureObject(feature)) > item.threshold; // Map all the returned, validated wkts back to features and calc the area of them and remove the very small ones
3536
+ }).map(wkt => {
3537
+ const newFeature = item.feature.clone();
3538
+ this._featureHelper.setId(newFeature);
3539
+ const g = wktFormat.readGeometry(wkt);
3540
+ newFeature.setGeometry(g);
3541
+ return newFeature;
3542
+ })).flatMap(f => f);
3543
+ const featuresToRemove = validatedItems.map(item => item.feature);
3544
+ this._drawLayerService.source.removeFeatures(featuresToRemove);
3545
+ this._undoRedo.disable();
3546
+ this._drawLayerService.source.addFeatures(featuresToAdd);
3547
+ this._undoRedo.enable();
3513
3548
  }
3514
3549
  });
3515
3550
  });