@dra2020/baseclient 1.0.59 → 1.0.62

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.
package/dist/csv/csv.d.ts CHANGED
@@ -1 +1,18 @@
1
- export {};
1
+ import * as Util from '../util/all';
2
+ export declare class ParseOne {
3
+ coder: Util.Coder;
4
+ fields: string[];
5
+ buf: Uint8Array;
6
+ n: number;
7
+ tok: Uint8Array;
8
+ toklen: number;
9
+ infield: boolean;
10
+ quote: number;
11
+ nwhite: number;
12
+ force: boolean;
13
+ constructor(coder: Util.Coder, line?: string);
14
+ set(line: string): void;
15
+ get length(): number;
16
+ pushtok(): void;
17
+ parse(): void;
18
+ }
@@ -48,6 +48,18 @@ export interface PolyDescription {
48
48
  export declare function polyDescribe(poly: any): PolyDescription;
49
49
  export declare function npoints(poly: any): number;
50
50
  export declare function polyTransform(poly: any, transformFn: any): any;
51
- export declare function featureRewind(poly: any): any;
51
+ export interface RewindOptions {
52
+ validateHoles?: boolean;
53
+ validateClose?: boolean;
54
+ canonical?: boolean;
55
+ }
56
+ interface Winding {
57
+ iPoly: number;
58
+ iRing: number;
59
+ direction: number;
60
+ }
61
+ export declare function polyRingWindings(poly: any): Winding[];
62
+ export declare function featureRewind(f: any, options?: RewindOptions): any;
52
63
  export declare function polyRewindRings(pp: any, bLog?: boolean): any;
53
64
  export declare function polyBadCoordinates(f: any): boolean;
65
+ export {};
package/lib/csv/csv.ts CHANGED
@@ -23,7 +23,7 @@ function isWhite(c: number): boolean
23
23
  return c === Space || c === Newline || c === Tab || c == CR;
24
24
  }
25
25
 
26
- class ParseOne
26
+ export class ParseOne
27
27
  {
28
28
  coder: Util.Coder;
29
29
  fields: string[]; // output
@@ -34,12 +34,15 @@ class ParseOne
34
34
  infield: boolean;
35
35
  quote: number;
36
36
  nwhite: number;
37
+ force: boolean;
37
38
 
38
39
  constructor(coder: Util.Coder, line?: string)
39
40
  {
40
41
  this.coder = coder;
41
42
  if (line)
42
43
  this.set(line);
44
+ else
45
+ this.fields = [];
43
46
  }
44
47
 
45
48
  set(line: string): void
@@ -52,18 +55,18 @@ class ParseOne
52
55
  this.infield = false;
53
56
  this.nwhite = 0;
54
57
  this.quote = 0;
58
+ this.force = false;
55
59
  this.parse();
56
60
  }
57
61
 
58
62
  get length(): number { return this.fields.length }
59
63
 
60
- pushtok(force: boolean): void
64
+ pushtok(): void
61
65
  {
62
66
  // Trim trailing whitespace
63
- if (!force)
64
- this.toklen -= this.nwhite;
67
+ this.toklen -= this.nwhite;
65
68
 
66
- if (this.toklen || force)
69
+ if (this.toklen || this.force)
67
70
  {
68
71
  this.fields.push(Util.u82s(this.coder, this.tok, 0, this.toklen));
69
72
  this.toklen = 0;
@@ -71,6 +74,7 @@ class ParseOne
71
74
  this.infield = false;
72
75
  this.nwhite = 0;
73
76
  this.quote = 0;
77
+ this.force = false;
74
78
  }
75
79
 
76
80
  parse(): void
@@ -79,13 +83,20 @@ class ParseOne
79
83
  {
80
84
  let c: number = this.buf[this.n++];
81
85
  if (this.quote && c === this.quote)
82
- this.pushtok(true);
86
+ {
87
+ this.quote = 0;
88
+ this.nwhite = 0;
89
+ }
83
90
  else if (c === Comma || c === Pipe)
84
- this.pushtok(true);
91
+ {
92
+ this.force = true;
93
+ this.pushtok();
94
+ this.force = true;
95
+ }
85
96
  else if (this.infield)
86
97
  {
87
98
  this.tok[this.toklen++] = c;
88
- if (isWhite(c))
99
+ if (!this.quote && isWhite(c))
89
100
  this.nwhite++;
90
101
  else
91
102
  this.nwhite = 0;
@@ -96,13 +107,15 @@ class ParseOne
96
107
  {
97
108
  this.quote = c;
98
109
  this.infield = true;
110
+ this.force = true;
99
111
  }
100
112
  else
101
113
  {
102
114
  this.infield = true;
103
115
  this.tok[this.toklen++] = c;
116
+ this.force = true;
104
117
  }
105
118
  }
106
- this.pushtok(false);
119
+ this.pushtok();
107
120
  }
108
121
  }
package/lib/poly/poly.ts CHANGED
@@ -667,30 +667,156 @@ function closePoly(poly: any): any
667
667
  return poly;
668
668
  }
669
669
 
670
+ function closeFeature(f: any): void
671
+ {
672
+ let d = Util.depthof(f.geometry.coordinates);
673
+ if (d === 4) closePoly(f.geometry.coordinates);
674
+ if (d === 5) f.geometry.coordinates.forEach(closePoly);
675
+ }
676
+
677
+ export interface RewindOptions
678
+ {
679
+ validateHoles?: boolean,
680
+ validateClose?: boolean,
681
+ canonical?: boolean,
682
+ }
683
+
684
+ function canonicalPoint(f: any, options: RewindOptions): any
685
+ {
686
+ if (options.canonical)
687
+ if (f && f.geometry && f.geometry.type === 'Point' && f.geometry.coordinates)
688
+ while (Array.isArray(f.geometry.coordinates[0]))
689
+ f.geometry.coordinates = f.geometry.coordinates[0];
690
+ return f;
691
+ }
692
+
693
+ //
694
+ // polyRingWindings: Return depth-first array of winding of each ring in the feature.
695
+ // value < 0: CW
696
+ // value > 0: CCW
697
+ //
698
+
699
+ interface Winding { iPoly: number, iRing: number, direction: number };
700
+
701
+ function misWound(w: Winding): boolean
702
+ {
703
+ return (((w.iRing == 0) && (w.direction > 0)) || ((w.iRing > 0) && (w.direction < 0)));
704
+ }
705
+
706
+ export function polyRingWindings(poly: any): Winding[]
707
+ {
708
+ let windings: Winding[] = [];
709
+
710
+ let pp = polyNormalize(poly);
711
+ if (pp == null || pp.buffer == null) return windings;
712
+
713
+ PP.polyPackEachRing(pp, (b: Float64Array, iPoly: number, iRing: number, iOffset: number, nPoints: number) =>
714
+ {
715
+ const iStart = iOffset;
716
+ const iEnd = iStart + (nPoints * 2) - 2;
717
+
718
+ // Determine the winding order of the ring
719
+ let direction = 0;
720
+
721
+ // Start at the second point
722
+ iOffset += 2;
723
+ for (; iOffset <= iEnd; iOffset += 2)
724
+ {
725
+ // The previous point
726
+ let jOffset = iOffset - 2;
727
+
728
+ // Sum over the edges
729
+ direction += twoTimesArea(b[iOffset], b[jOffset], b[iOffset+1], b[jOffset+1]);
730
+ }
731
+
732
+ // Implicitly close the ring, if necessary
733
+ if (nPoints > 2 && (b[iStart] != b[iEnd] || b[iStart + 1] != b[iEnd + 1]))
734
+ direction += twoTimesArea(b[iStart], b[iEnd], b[iStart + 1], b[iEnd + 1]);
735
+
736
+ windings.push({ iPoly: iPoly, iRing: iRing, direction: direction });
737
+ });
738
+ return windings;
739
+ }
740
+
741
+
670
742
  // This mutates the passed in feature to ensure it is correctly wound
671
743
  // For convenience, passed back the value provided.
672
- export function featureRewind(poly: any): any
744
+ //
745
+ export function featureRewind(f: any, options?: RewindOptions): any
673
746
  {
674
- let pp = polyNormalize(poly);
675
- if (pp == null) return null;
676
- polyRewindRings(pp);
677
- if (poly.type === 'Feature')
747
+ options = Util.shallowAssignImmutable({ validateHoles: true, validateClose: true, canonical: true }, options);
748
+
749
+ if (!f) return null;
750
+
751
+ // Has to be an unpacked feature
752
+ if (f.type !== 'Feature') throw 'featureRewind: must be a valid GeoJSON feature';
753
+ if (!f.geometry || f.geometry.packed) throw 'featureRewind: only valid on unpacked features';
754
+
755
+ // Non polygons are simpler
756
+ if (f.geometry.type !== 'Polygon' && f.geometry.type !== 'MultiPolygon') return canonicalPoint(f, options);
757
+
758
+ // Make sure polygon is closed (first === last)
759
+ if (options.validateClose) closeFeature(f);
760
+
761
+ // Check if multi-polygon is really polygon with holes
762
+ // Only applies to multi-polygons with no holes
763
+ let d = Util.depthof(f.geometry.coordinates);
764
+ let windings = polyRingWindings(f);
765
+ if (options.validateHoles && d === 5 && windings.length)
678
766
  {
679
- if (poly.geometry.packed !== pp)
767
+ // If there are explicit holes, don't look for implicit ones
768
+ let nHoles = 0; windings.forEach(w => { if (w.iRing) nHoles++ });
769
+ if (nHoles == 0 && !misWound(windings[0]))
770
+ {
771
+ // Looking for pattern: of (G (B*))*
772
+ // To convert to ((GH*))*
773
+ let iWinding = 0;
774
+ let polys = f.geometry.coordinates;
775
+ let iPoly: number;
776
+ for (; iWinding < windings.length; iWinding++)
777
+ {
778
+ let good = !misWound(windings[iWinding]);
779
+ if (!good && iWinding == 0) // First is miswound - no good
780
+ break;
781
+ if (good)
782
+ iPoly = iWinding;
783
+ else
784
+ {
785
+ polys[iPoly].push(polys[iWinding][0]);
786
+ polys[iWinding] = null;
787
+ }
788
+ }
789
+ f.geometry.coordinates = polys.filter((p: any) => p != null);
790
+ }
791
+
792
+ // Degenerate multi-polygon
793
+ if (f.geometry.coordinates.length == 1)
680
794
  {
681
- poly.geometry.coordinates = PP.polyUnpack(pp);
682
- // Also make sure first === last coordinate
683
- let d = Util.depthof(poly.geometry.coordinates);
684
- if (d === 4) closePoly(poly.geometry.coordinates);
685
- else if (d === 5) poly.geometry.coordinates.forEach(closePoly);
686
- poly.geometry.type = d === 4 ? 'Polygon' : 'MultiPolygon';
795
+ f.geometry.type = 'Polygon';
796
+ f.geometry.coordinates = f.geometry.coordinates[0];
687
797
  }
688
- return poly;
689
798
  }
690
- else if (poly === pp)
691
- return pp;
692
- else
693
- return PP.polyUnpack(pp);
799
+
800
+ // OK, now go through each ring
801
+ let polys = f.geometry.coordinates;
802
+ windings = polyRingWindings(f);
803
+ windings.forEach((w: Winding) => {
804
+ let good = !misWound(w);
805
+ if (!good)
806
+ {
807
+ let ring = polys[w.iPoly][w.iRing];
808
+ let iFront = 0;
809
+ let iBack = ring.length-1;
810
+ for (; iFront < iBack; iFront++, iBack--)
811
+ {
812
+ let tmp = ring[iFront];
813
+ ring[iFront] = ring[iBack];
814
+ ring[iBack] = tmp;
815
+ }
816
+ }
817
+ });
818
+
819
+ return f;
694
820
  }
695
821
 
696
822
  //
package/lib/poly/topo.ts CHANGED
@@ -84,7 +84,7 @@ function correctGeometry(f: any): any
84
84
  }
85
85
  else
86
86
  // TopoJSON does not guarantee proper winding order which messes up later processing. Fix it.
87
- P.featureRewind(f);
87
+ P.featureRewind(f, { validateHoles: false } );
88
88
 
89
89
  return f;
90
90
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dra2020/baseclient",
3
- "version": "1.0.59",
3
+ "version": "1.0.62",
4
4
  "description": "Utility functions for Javascript projects.",
5
5
  "main": "dist/baseclient.js",
6
6
  "types": "./dist/all/all.d.ts",