@rural-environmental-registry/map_component 1.1.0 → 2.0.0

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.
Files changed (38) hide show
  1. package/README.md +1 -0
  2. package/dist/handlers/measureToolsHandler.d.ts +1 -0
  3. package/dist/index.css +1 -1
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.mjs +16337 -14954
  6. package/dist/types/index.d.ts +118 -15
  7. package/dist/utils/geometryFileParseResult.d.ts +8 -0
  8. package/dist/utils/layersConfigNormalizer.d.ts +16 -0
  9. package/dist/utils/menuHistory.d.ts +3 -3
  10. package/dist/utils/normalizeGeoJsonToFeatures.d.ts +2 -0
  11. package/dist/utils/parseGeoJsonFile.d.ts +2 -0
  12. package/dist/utils/parseKmlFile.d.ts +2 -0
  13. package/dist/utils/parseShapefileZip.d.ts +2 -8
  14. package/dist/utils/{validateShapefileGeometry.d.ts → validateImportedPolygonGeometry.d.ts} +2 -2
  15. package/package.json +3 -2
  16. package/src/components/MapDPG.vue +90 -2
  17. package/src/components/coordinate/CoordinatePanel.vue +31 -8
  18. package/src/components/fa-icon/FontAwesomeIcon.vue +35 -5
  19. package/src/components/map/LeafletMap.vue +3 -1
  20. package/src/components/menu/ChildMenu.vue +307 -27
  21. package/src/components/menu/LayerMenu.vue +232 -37
  22. package/src/components/menu/ParentMenu.vue +261 -27
  23. package/src/components/menu/SectionMenu.vue +512 -0
  24. package/src/constants/coordinatePanel.ts +6 -5
  25. package/src/handlers/mapToolsHandler.ts +3 -1
  26. package/src/handlers/measureToolsHandler.ts +36 -27
  27. package/src/handlers/toolsConstants.ts +1 -0
  28. package/src/index.ts +47 -0
  29. package/src/types/index.ts +149 -14
  30. package/src/utils/geometryFileParseResult.ts +5 -0
  31. package/src/utils/layersConfigNormalizer.ts +273 -0
  32. package/src/utils/menuHistory.ts +35 -19
  33. package/src/utils/normalizeGeoJsonToFeatures.ts +52 -0
  34. package/src/utils/parseGeoJsonFile.ts +23 -0
  35. package/src/utils/parseKmlFile.ts +31 -0
  36. package/src/utils/parseShapefileZip.ts +4 -31
  37. package/src/utils/validateImportedPolygonGeometry.ts +62 -0
  38. package/src/utils/validateShapefileGeometry.ts +0 -54
@@ -0,0 +1,23 @@
1
+ import { normalizeGeoJsonToFeatures } from './normalizeGeoJsonToFeatures'
2
+ import type { GeometryFileParseResult } from './geometryFileParseResult'
3
+
4
+ export async function parseGeoJsonFile(file: File): Promise<GeometryFileParseResult> {
5
+ const fileName = file.name.toLowerCase()
6
+ if (!fileName.endsWith('.geojson') && !fileName.endsWith('.json')) {
7
+ return { ok: false, error: 'The file must be a .geojson or .json file' }
8
+ }
9
+
10
+ try {
11
+ const content = await file.text()
12
+ const parsed = JSON.parse(content) as unknown
13
+ const features = normalizeGeoJsonToFeatures(parsed)
14
+
15
+ if (features.length === 0) {
16
+ return { ok: false, error: 'The GeoJSON file contains no geometries' }
17
+ }
18
+
19
+ return { ok: true, features }
20
+ } catch {
21
+ return { ok: false, error: 'Could not read the file. Check that it contains valid GeoJSON' }
22
+ }
23
+ }
@@ -0,0 +1,31 @@
1
+ import { kml } from '@tmcw/togeojson'
2
+ import { normalizeGeoJsonToFeatures } from './normalizeGeoJsonToFeatures'
3
+ import type { GeometryFileParseResult } from './geometryFileParseResult'
4
+
5
+ export async function parseKmlFile(file: File): Promise<GeometryFileParseResult> {
6
+ const fileName = file.name.toLowerCase()
7
+ if (!fileName.endsWith('.kml')) {
8
+ return { ok: false, error: 'The file must be a .kml file' }
9
+ }
10
+
11
+ try {
12
+ const content = await file.text()
13
+ const xmlDoc = new DOMParser().parseFromString(content, 'text/xml')
14
+ const parserError = xmlDoc.querySelector('parsererror')
15
+
16
+ if (parserError) {
17
+ return { ok: false, error: 'Could not read the file. Check that it contains valid KML' }
18
+ }
19
+
20
+ const geojson = kml(xmlDoc)
21
+ const features = normalizeGeoJsonToFeatures(geojson)
22
+
23
+ if (features.length === 0) {
24
+ return { ok: false, error: 'The KML file contains no geometries' }
25
+ }
26
+
27
+ return { ok: true, features }
28
+ } catch {
29
+ return { ok: false, error: 'Could not read the file. Check that it contains valid KML' }
30
+ }
31
+ }
@@ -1,35 +1,8 @@
1
1
  import shp from 'shpjs'
2
- import type { Feature, FeatureCollection } from 'geojson'
2
+ import { normalizeGeoJsonToFeatures } from './normalizeGeoJsonToFeatures'
3
+ import type { GeometryFileParseResult } from './geometryFileParseResult'
3
4
 
4
- export type ShapefileParseResult =
5
- | { ok: true; features: Feature[] }
6
- | { ok: false; error: string }
7
-
8
- function isFeatureCollection(value: unknown): value is FeatureCollection {
9
- return (
10
- typeof value === 'object' &&
11
- value !== null &&
12
- 'type' in value &&
13
- (value as FeatureCollection).type === 'FeatureCollection'
14
- )
15
- }
16
-
17
- function normalizeToFeatures(geojson: FeatureCollection | Feature | Feature[] | FeatureCollection[]): Feature[] {
18
- if (Array.isArray(geojson)) {
19
- return geojson.flatMap((item) => {
20
- if (isFeatureCollection(item)) {
21
- return item.features
22
- }
23
- return [item as Feature]
24
- })
25
- }
26
-
27
- if (isFeatureCollection(geojson)) {
28
- return geojson.features
29
- }
30
-
31
- return [geojson as Feature]
32
- }
5
+ export type ShapefileParseResult = GeometryFileParseResult
33
6
 
34
7
  export async function parseShapefileZip(file: File): Promise<ShapefileParseResult> {
35
8
  const fileName = file.name.toLowerCase()
@@ -40,7 +13,7 @@ export async function parseShapefileZip(file: File): Promise<ShapefileParseResul
40
13
  try {
41
14
  const buffer = await file.arrayBuffer()
42
15
  const geojson = await shp(buffer)
43
- const features = normalizeToFeatures(geojson as FeatureCollection | Feature | Feature[] | FeatureCollection[])
16
+ const features = normalizeGeoJsonToFeatures(geojson)
44
17
 
45
18
  if (features.length === 0) {
46
19
  return { ok: false, error: 'The shapefile contains no geometries' }
@@ -0,0 +1,62 @@
1
+ import { area, booleanValid, kinks } from '@turf/turf'
2
+ import type { Feature, MultiPolygon, Polygon, Position } from 'geojson'
3
+
4
+ export type ImportedPolygonValidationResult =
5
+ | { ok: true; feature: Feature<Polygon | MultiPolygon> }
6
+ | { ok: false; error: string }
7
+
8
+ const COORD_TOLERANCE = 1e-9
9
+
10
+ function coordsEqual(a: Position, b: Position): boolean {
11
+ return Math.abs(a[0] - b[0]) <= COORD_TOLERANCE && Math.abs(a[1] - b[1]) <= COORD_TOLERANCE
12
+ }
13
+
14
+ function isRingClosed(ring: Position[]): boolean {
15
+ if (ring.length < 4) return false
16
+ return coordsEqual(ring[0], ring[ring.length - 1])
17
+ }
18
+
19
+ function validateRingsClosed(geometry: Polygon | MultiPolygon): boolean {
20
+ if (!Array.isArray(geometry?.coordinates)) return false
21
+ const polygons = geometry.type === 'Polygon' ? [geometry.coordinates] : geometry.coordinates
22
+ return polygons.every(polygon => polygon.every(ring => isRingClosed(ring)))
23
+ }
24
+
25
+ export function validateImportedPolygonGeometry(features: Feature[]): ImportedPolygonValidationResult {
26
+ try {
27
+ if (features.length !== 1) {
28
+ return { ok: false, error: 'The file must contain exactly one geometry' }
29
+ }
30
+
31
+ const feature = features[0]
32
+ const geometry = feature.geometry
33
+
34
+ if (!geometry || (geometry.type !== 'Polygon' && geometry.type !== 'MultiPolygon')) {
35
+ return { ok: false, error: 'Geometry must be Polygon or MultiPolygon' }
36
+ }
37
+
38
+ if (!validateRingsClosed(geometry)) {
39
+ return { ok: false, error: 'Polygon ring is not closed or is invalid' }
40
+ }
41
+
42
+ if (!booleanValid(geometry)) {
43
+ return { ok: false, error: 'Invalid geometry' }
44
+ }
45
+
46
+ if (kinks(geometry).features.length > 0) {
47
+ return { ok: false, error: 'Geometry has self-intersection' }
48
+ }
49
+
50
+ if (area(geometry) <= 0) {
51
+ return { ok: false, error: 'Polygon has zero area' }
52
+ }
53
+
54
+ return { ok: true, feature: feature as Feature<Polygon | MultiPolygon> }
55
+ } catch {
56
+ return {
57
+ ok: false,
58
+ error:
59
+ 'An error occurred while validating the geometry. Ensure the file contains valid coordinates.'
60
+ }
61
+ }
62
+ }
@@ -1,54 +0,0 @@
1
- import { area, booleanValid, kinks } from '@turf/turf'
2
- import type { Feature, MultiPolygon, Polygon, Position } from 'geojson'
3
-
4
- export type ShapefileValidationResult =
5
- | { ok: true; feature: Feature<Polygon | MultiPolygon> }
6
- | { ok: false; error: string }
7
-
8
- const COORD_TOLERANCE = 1e-9
9
-
10
- function coordsEqual(a: Position, b: Position): boolean {
11
- return Math.abs(a[0] - b[0]) <= COORD_TOLERANCE && Math.abs(a[1] - b[1]) <= COORD_TOLERANCE
12
- }
13
-
14
- function isRingClosed(ring: Position[]): boolean {
15
- if (ring.length < 4) return false
16
- return coordsEqual(ring[0], ring[ring.length - 1])
17
- }
18
-
19
- function validateRingsClosed(geometry: Polygon | MultiPolygon): boolean {
20
- if (!Array.isArray(geometry?.coordinates)) return false
21
- const polygons = geometry.type === 'Polygon' ? [geometry.coordinates] : geometry.coordinates
22
- return polygons.every(polygon => polygon.every(ring => isRingClosed(ring)))
23
- }
24
-
25
- export function validateShapefileGeometry(features: Feature[]): ShapefileValidationResult {
26
- if (features.length !== 1) {
27
- return { ok: false, error: 'The shapefile must contain exactly one geometry' }
28
- }
29
-
30
- const feature = features[0]
31
- const geometry = feature.geometry
32
-
33
- if (!geometry || (geometry.type !== 'Polygon' && geometry.type !== 'MultiPolygon')) {
34
- return { ok: false, error: 'Geometry must be Polygon or MultiPolygon' }
35
- }
36
-
37
- if (!validateRingsClosed(geometry)) {
38
- return { ok: false, error: 'Polygon ring is not closed or is invalid' }
39
- }
40
-
41
- if (!booleanValid(geometry)) {
42
- return { ok: false, error: 'Invalid geometry' }
43
- }
44
-
45
- if (kinks(geometry).features.length > 0) {
46
- return { ok: false, error: 'Geometry has self-intersection' }
47
- }
48
-
49
- if (area(geometry) <= 0) {
50
- return { ok: false, error: 'Polygon has zero area' }
51
- }
52
-
53
- return { ok: true, feature: feature as Feature<Polygon | MultiPolygon> }
54
- }