@theseam/ui-common 2.0.1-beta.115 → 2.0.1-beta.119

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.
@@ -711,32 +711,17 @@ function openBlob(blob, target, filename) {
711
711
  // }
712
712
  }
713
713
 
714
- /** Returns object without property */
715
- function withoutProperty(obj, propName) {
716
- const { [propName]: _, ...without } = obj;
717
- return without;
718
- }
719
- /** Returns object without properties */
720
- function withoutProperties(obj, propNames) {
721
- let without = obj;
722
- for (const propName of propNames) {
723
- without = withoutProperty(without, propName);
724
- }
725
- return without;
726
- }
727
- /** Delete property of object */
728
- function deleteProperty(obj, propName) {
729
- if (Object.prototype.hasOwnProperty.call(obj, propName)) {
730
- delete obj[propName];
731
- }
732
- }
733
- /** Delete properties of object */
734
- function deleteProperties(obj, propNames) {
735
- for (const propName of propNames) {
736
- deleteProperty(obj, propName);
737
- }
738
- }
739
-
714
+ /**
715
+ * Property key under which `readGeoFile` records the archive member a feature
716
+ * came from, for shapefile archives only.
717
+ *
718
+ * A zip can hold one shapefile per field, and once their features are
719
+ * concatenated the member name is the only thing distinguishing them — for
720
+ * some producer exports it is the only usable name at all, because the `.dbf`
721
+ * carries nothing but numbers. The value is the member's basename, without its
722
+ * directory or extension.
723
+ */
724
+ const GEO_FILE_SOURCE_NAME_PROPERTY = 'seamSourceFileName';
740
725
  /**
741
726
  * Reads a File, or buffer of file content, in GeoJSON or ESRI Shapefile format
742
727
  * and returns a GeoJSON `FeatureCollection`.
@@ -775,17 +760,73 @@ async function parseShpFile(buffer) {
775
760
  return featCollection;
776
761
  }
777
762
  async function parseShpZip(buffer) {
778
- let featCollection = await parseZip(buffer);
779
- if (Array.isArray(featCollection)) {
780
- if (featCollection.length === 0) {
781
- throw Error(`Shape data not found.`);
763
+ const parsed = await parseZipLayers(buffer);
764
+ const features = [];
765
+ let coercedAny = false;
766
+ for (const layer of parsed) {
767
+ const collection = coerceFeatureCollection(layer);
768
+ if (collection === null) {
769
+ // `parseZip` also reports `.json` members as layers, and one can hold
770
+ // something that is not a FeatureCollection. Skip it rather than fail an
771
+ // archive whose shapefiles are perfectly good.
772
+ continue;
782
773
  }
783
- else if (featCollection.length > 1) {
784
- throw Error(`Multiple shape files not supported.`);
774
+ coercedAny = true;
775
+ const sourceName = basename(layer.fileName);
776
+ for (const feature of collection.features) {
777
+ features.push(sourceName === undefined
778
+ ? feature
779
+ : {
780
+ ...feature,
781
+ properties: {
782
+ ...feature.properties,
783
+ [GEO_FILE_SOURCE_NAME_PROPERTY]: sourceName,
784
+ },
785
+ });
785
786
  }
786
- featCollection = featCollection[0];
787
787
  }
788
- return withoutProperty(featCollection, 'fileName');
788
+ // Not "features.length === 0": a member that coerced fine but held zero
789
+ // records (an empty selection, legally exported) is a successful empty
790
+ // import, not a failure. Only "every member failed to coerce" means the
791
+ // archive held no usable shape data.
792
+ if (!coercedAny) {
793
+ throw Error(`Shape data not found.`);
794
+ }
795
+ // Built fresh, so the `fileName` shpjs sets on each layer is not carried out
796
+ // of here on the collection itself.
797
+ return { type: 'FeatureCollection', features };
798
+ }
799
+ /**
800
+ * Normalizes `parseZip`'s return to an array. It hands back the collection
801
+ * itself for a single-shapefile archive and an array for a multi-shapefile
802
+ * one, and throws its own `no layers founds` for an archive holding neither a
803
+ * `.shp` nor a `.json`.
804
+ */
805
+ async function parseZipLayers(buffer) {
806
+ let parsed;
807
+ try {
808
+ parsed = await parseZip(buffer);
809
+ }
810
+ catch (err) {
811
+ if (err instanceof Error && err.message === 'no layers founds') {
812
+ throw Error(`Shape data not found.`);
813
+ }
814
+ throw err;
815
+ }
816
+ return Array.isArray(parsed) ? parsed : [parsed];
817
+ }
818
+ /**
819
+ * Member name without its directory, as a feature can use it. Strips only the
820
+ * directory: shpjs has already stripped the extension from `layer.fileName`
821
+ * by the time it reaches here, so there is none left to remove.
822
+ */
823
+ function basename(fileName) {
824
+ if (fileName === undefined || fileName.length === 0) {
825
+ return undefined;
826
+ }
827
+ const lastSlash = Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));
828
+ const name = lastSlash === -1 ? fileName : fileName.slice(lastSlash + 1);
829
+ return name.length === 0 ? undefined : name;
789
830
  }
790
831
  function parseGeoJson(buffer) {
791
832
  const json = JSON.parse(new TextDecoder().decode(buffer));
@@ -1103,6 +1144,32 @@ function loadStyleSheet(path) {
1103
1144
  });
1104
1145
  }
1105
1146
 
1147
+ /** Returns object without property */
1148
+ function withoutProperty(obj, propName) {
1149
+ const { [propName]: _, ...without } = obj;
1150
+ return without;
1151
+ }
1152
+ /** Returns object without properties */
1153
+ function withoutProperties(obj, propNames) {
1154
+ let without = obj;
1155
+ for (const propName of propNames) {
1156
+ without = withoutProperty(without, propName);
1157
+ }
1158
+ return without;
1159
+ }
1160
+ /** Delete property of object */
1161
+ function deleteProperty(obj, propName) {
1162
+ if (Object.prototype.hasOwnProperty.call(obj, propName)) {
1163
+ delete obj[propName];
1164
+ }
1165
+ }
1166
+ /** Delete properties of object */
1167
+ function deleteProperties(obj, propNames) {
1168
+ for (const propName of propNames) {
1169
+ deleteProperty(obj, propName);
1170
+ }
1171
+ }
1172
+
1106
1173
  function notNullOrUndefinedOrEmpty(value, trim = true) {
1107
1174
  return (value !== null &&
1108
1175
  value !== undefined &&
@@ -1494,5 +1561,5 @@ function sanitizeFilename(filename, options) {
1494
1561
  * Generated bundle index. Do not edit.
1495
1562
  */
1496
1563
 
1497
- export { IS_FEATURE_COLLECTION_VALIDATOR_NAME, IS_ONLY_GEOMETRY_TYPES_VALIDATOR_NAME, MIN_MAX_POINTS_VALIDATOR_NAME, NO_EMPTY_FEATURE_COLLECTION_VALIDATOR_NAME, NO_INNER_RINGS_VALIDATOR_NAME, NO_KINKS_VALIDATOR_NAME, PollingTickerOptions, Refreshable, activatedRoutesWithDataProperty, addHoleToPolygon, arrayMoveImmutable, arrayMoveMutable, calcPercentage, closePolygons, coerceFeatureCollection, createPadding, deleteProperties, deleteProperty, fileBufferToBlob, fileBufferToObjectUrl, fileDataFromBuffer, fractionalDigitsCount, geoJsonToArea, getAttribute, getClosestWidgetCdkDrag, getControlName, getControlPath, hasAttribute, hasProperty, hasRequiredControl, isAbsoluteUrl, isEmptyInputValue, isEmptyUrlRoute, isFeatureCollectionValidator, isNullOrUndefined, isNullOrUndefinedOrEmpty, isNumeric, isOnlyGeometryTypes, isOnlyGeometryTypesValidator, leafChildRoute, loadStyle, loadStyleSheet, mapEach, mergePolygons, minMaxPointsValidator, noEmptyFeatureCollectionValidator, noInnerRingsValidator, noKinksValidator, notNullOrUndefined, notNullOrUndefinedOrEmpty, observeControlIsDifferent, observeControlStatus, observeControlValid, observeControlValue, observeControlValueChange, observeQueryList, openBlob, padEnd, padStart, phoneNumberMask, pollingTicker, polygonContains, polygonHasMinDistinctVertices, polygonViolatesMinMax, readFileAsDataUrlAsync, readFileAsync, readGeoFile, ringIsClockwise, routeSnapshotPathFull, routeSnapshotPathRelative, sanitizeFilename, splitMultiPolygons, stripOuter, subscriberCount, tapFirst, toggleAttribute, trimRepeated, waitOnConditionAsync, waitOnNonPendingStatus, willHaveDataProp, withoutProperties, withoutProperty, wrapIntoObservable };
1564
+ export { GEO_FILE_SOURCE_NAME_PROPERTY, IS_FEATURE_COLLECTION_VALIDATOR_NAME, IS_ONLY_GEOMETRY_TYPES_VALIDATOR_NAME, MIN_MAX_POINTS_VALIDATOR_NAME, NO_EMPTY_FEATURE_COLLECTION_VALIDATOR_NAME, NO_INNER_RINGS_VALIDATOR_NAME, NO_KINKS_VALIDATOR_NAME, PollingTickerOptions, Refreshable, activatedRoutesWithDataProperty, addHoleToPolygon, arrayMoveImmutable, arrayMoveMutable, calcPercentage, closePolygons, coerceFeatureCollection, createPadding, deleteProperties, deleteProperty, fileBufferToBlob, fileBufferToObjectUrl, fileDataFromBuffer, fractionalDigitsCount, geoJsonToArea, getAttribute, getClosestWidgetCdkDrag, getControlName, getControlPath, hasAttribute, hasProperty, hasRequiredControl, isAbsoluteUrl, isEmptyInputValue, isEmptyUrlRoute, isFeatureCollectionValidator, isNullOrUndefined, isNullOrUndefinedOrEmpty, isNumeric, isOnlyGeometryTypes, isOnlyGeometryTypesValidator, leafChildRoute, loadStyle, loadStyleSheet, mapEach, mergePolygons, minMaxPointsValidator, noEmptyFeatureCollectionValidator, noInnerRingsValidator, noKinksValidator, notNullOrUndefined, notNullOrUndefinedOrEmpty, observeControlIsDifferent, observeControlStatus, observeControlValid, observeControlValue, observeControlValueChange, observeQueryList, openBlob, padEnd, padStart, phoneNumberMask, pollingTicker, polygonContains, polygonHasMinDistinctVertices, polygonViolatesMinMax, readFileAsDataUrlAsync, readFileAsync, readGeoFile, ringIsClockwise, routeSnapshotPathFull, routeSnapshotPathRelative, sanitizeFilename, splitMultiPolygons, stripOuter, subscriberCount, tapFirst, toggleAttribute, trimRepeated, waitOnConditionAsync, waitOnNonPendingStatus, willHaveDataProp, withoutProperties, withoutProperty, wrapIntoObservable };
1498
1565
  //# sourceMappingURL=theseam-ui-common-utils.mjs.map