11<script setup lang="ts">
2- import { getGeoJSONCoordinates } from ' @/components/common/map/createFeatureCollectionAndProps.ts' ;
2+ import { createGeoJSONGeometry } from ' @/components/common/map/createFeatureCollectionAndProps.ts' ;
33import {
44 DRAW_FEATURE_TYPES ,
55 type DrawFeatureType ,
66} from ' @/components/common/map/useMapInteractions.ts' ;
77import { isCoordsEqual } from ' @/components/common/map/vertex-geometry.ts' ;
8- import type { FeatureCollection , LineString , Point , Polygon } from ' geojson' ;
8+ import type { FeatureCollection , Geometry , LineString , Point , Polygon } from ' geojson' ;
99import { fromLonLat } from ' ol/proj' ;
1010import Button from ' primevue/button' ;
1111import Dialog from ' primevue/dialog' ;
@@ -43,7 +43,7 @@ const selectFile = (event: Event) => {
4343 error .value = null ;
4444};
4545
46- const parseFileCoordinates = async (file : File ): Promise <Coordinate [] | undefined > => {
46+ const parseFileCoordinates = async (file : File ): Promise <Geometry | undefined > => {
4747 try {
4848 const text = await file .text ();
4949 if (! text .trim ()) {
@@ -68,17 +68,12 @@ const parseFileCoordinates = async (file: File): Promise<Coordinate[] | undefine
6868 }
6969};
7070
71- const parseGeoJSONCoordinates = (text : string ): Coordinate [] | undefined => {
71+ const parseGeoJSONCoordinates = (text : string ): Geometry | undefined => {
7272 const geojson = JSON .parse (text ) as FeatureCollection <LineString | Point | Polygon >;
73- const coords = geojson ?.features ?.[0 ]?.geometry ?.coordinates as Coordinate [] | undefined ;
74- if (! Array .isArray (coords )) {
75- return ;
76- }
77-
78- return coords ;
73+ return geojson ?.features ?.[0 ]?.geometry ;
7974};
8075
81- const parseCSVGeometry = (text : string ): Coordinate [] | undefined => {
76+ const parseCSVGeometry = (text : string ): Geometry | undefined => {
8277 const lines = text .split (/ \r ? \n / ).filter ((line ) => line .trim ().length > 0 );
8378 if (lines .length < 2 ) {
8479 return ;
@@ -92,7 +87,7 @@ const parseCSVGeometry = (text: string): Coordinate[] | undefined => {
9287
9388 const firstDataRow = lines [1 ]?.split (' ,' ) ?? [];
9489 const geometryValue = firstDataRow [geometryIndex ]?.trim () ?? ' ' ;
95- return getGeoJSONCoordinates (geometryValue );
90+ return createGeoJSONGeometry (geometryValue ) as Geometry | undefined ;
9691};
9792
9893const parsePastedValue = () => {
@@ -101,58 +96,69 @@ const parsePastedValue = () => {
10196 return ;
10297 }
10398
104- return getGeoJSONCoordinates (value );
99+ return createGeoJSONGeometry (value ) as Geometry | undefined ;
105100};
106101
107- const isExpectedFeatureType = (coords : Coordinate | Coordinate [] | Coordinate [][]) => {
108- const isPoint = ! props .drawFeatureType && ! Array .isArray (coords [0 ]) && coords .length > 2 ;
109- if (isPoint ) {
110- return true ;
102+ const getValidCoordinates = (geometry : LineString | Point | Polygon | undefined ) => {
103+ if (! geometry ?.coordinates ) {
104+ return ;
105+ }
106+
107+ const coords = geometry .coordinates as Coordinate | Coordinate [] | Coordinate [][];
108+ if (geometry .type === ' Point' && ! props .drawFeatureType && ! Array .isArray (coords [0 ])) {
109+ return fromLonLat (coords as Coordinate );
111110 }
112111
113112 const hasRing = Array .isArray (coords [0 ]) && Array .isArray (coords [0 ][0 ]);
114- const flatCoords = (hasRing ? coords [0 ] : coords ) as Coordinate [];
113+ let flatCoords = (hasRing ? coords [0 ] : coords ) as Coordinate [];
115114 if (! flatCoords ?.length ) {
116- return false ;
115+ return ;
117116 }
118117
118+ flatCoords = flatCoords .map ((c ) => fromLonLat (c ));
119119 const isClosed = isCoordsEqual (flatCoords [0 ], flatCoords [flatCoords .length - 1 ]);
120- if (props .drawFeatureType === DRAW_FEATURE_TYPES .TRACE && ! isClosed && flatCoords .length >= 2 ) {
121- return true ;
120+ if (
121+ geometry .type === ' LineString' &&
122+ props .drawFeatureType === DRAW_FEATURE_TYPES .TRACE &&
123+ ! isClosed &&
124+ flatCoords .length >= 2
125+ ) {
126+ return flatCoords ;
122127 }
123128
124- return props .drawFeatureType === DRAW_FEATURE_TYPES .SHAPE && isClosed && flatCoords .length >= 3 ;
129+ if (
130+ geometry .type === ' Polygon' &&
131+ props .drawFeatureType === DRAW_FEATURE_TYPES .SHAPE &&
132+ isClosed &&
133+ flatCoords .length >= 3
134+ ) {
135+ return [flatCoords ];
136+ }
125137};
126138
127139const save = async () => {
128140 error .value = null ;
129- let coordinates : Coordinate [] | undefined ;
141+ let geometry ;
130142 if (selectedFile .value ) {
131- coordinates = await parseFileCoordinates (selectedFile .value );
143+ geometry = await parseFileCoordinates (selectedFile .value );
132144 } else if (hasPastedValue .value ) {
133- coordinates = parsePastedValue ();
145+ geometry = parsePastedValue ();
134146 }
135147
148+ const coordinates = getValidCoordinates (geometry as LineString | Point | Polygon | undefined );
136149 if (! coordinates ?.length ) {
137- // TODO: translations
138- error .value ?? = ' No valid coordinates found.' ;
139- return ;
140- }
141-
142- coordinates = coordinates .map ((coord ) => fromLonLat (coord ));
143- if (! isExpectedFeatureType (coordinates )) {
144150 // TODO: translations
145151 error .value ?? = ' Incorrect geometry type.' ;
146152 return ;
147153 }
148154
149- emit (' save' , coordinates );
150155 close ();
156+ emit (' save' , coordinates );
151157};
152158
153159const close = () => {
154- emit (' update:visible' , false );
155160 reset ();
161+ emit (' update:visible' , false );
156162};
157163
158164const reset = () => {
@@ -182,6 +188,7 @@ watch(pasteValue, (newVal) => {
182188 class =" map-paste-dialog"
183189 :draggable =" false"
184190 @update:visible =" emit('update:visible', $event)"
191+ @after-hide =" reset"
185192 >
186193 <template #header >
187194 <!-- TODO: translations -->
@@ -198,6 +205,8 @@ watch(pasteValue, (newVal) => {
198205 <div class =" dialog-field-container" >
199206 <!-- TODO: translations -->
200207 <label >Or upload a GeoJSON or a CSV file</label >
208+ <!-- TODO: translations -->
209+ <span v-if =" selectedFile" ><i >File uploaded</i ></span >
201210 <Button outlined severity =" contrast" @click =" openFileChooser" >
202211 <IconSVG name =" mdiUpload" />
203212 <!-- TODO: translations -->
@@ -209,12 +218,14 @@ watch(pasteValue, (newVal) => {
209218 type =" file"
210219 accept =" .geojson,.csv,application/json,text/csv"
211220 @change =" selectFile"
212- / >
221+ >
213222 </div >
214223 </template >
215224
216225 <template #footer >
217- <p v-if =" error?.length" class =" coords-error-message" >{{ error }}</p >
226+ <p v-if =" error?.length" class =" coords-error-message" >
227+ {{ error }}
228+ </p >
218229 <Button label =" Save" :disabled =" !selectedFile && !hasPastedValue" @click =" save" />
219230 </template >
220231 </Dialog >
@@ -243,6 +254,7 @@ watch(pasteValue, (newVal) => {
243254}
244255
245256.coords-error-message {
257+ display : block ;
246258 color : var (--odk-error-text-color );
247259 margin-bottom : 10px ;
248260}
0 commit comments