@dra2020/baseclient 1.0.60 → 1.0.63
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/baseclient.js +112 -38
- package/dist/baseclient.js.map +1 -1
- package/dist/poly/poly.d.ts +13 -1
- package/lib/poly/poly.ts +143 -17
- package/lib/poly/topo.ts +1 -22
- package/package.json +1 -1
package/dist/poly/poly.d.ts
CHANGED
|
@@ -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
|
|
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/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
|
-
|
|
744
|
+
//
|
|
745
|
+
export function featureRewind(f: any, options?: RewindOptions): any
|
|
673
746
|
{
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
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
|
-
|
|
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
|
-
|
|
682
|
-
|
|
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
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
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
|
@@ -65,28 +65,7 @@ function ringsCancel(outerPoly: any, innerRing: any): boolean
|
|
|
65
65
|
|
|
66
66
|
function correctGeometry(f: any): any
|
|
67
67
|
{
|
|
68
|
-
|
|
69
|
-
{
|
|
70
|
-
let multiPoly = f.geometry.coordinates;
|
|
71
|
-
|
|
72
|
-
// Convert degenerate MultiPolygon to Polygon
|
|
73
|
-
if (multiPoly.length == 1)
|
|
74
|
-
{
|
|
75
|
-
f.geometry.type = 'Polygon';
|
|
76
|
-
f.geometry.coordinates = multiPoly[0];
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (f && f.geometry && f.geometry.type === 'Point' && f.geometry.coordinates)
|
|
81
|
-
{
|
|
82
|
-
while (Array.isArray(f.geometry.coordinates[0]))
|
|
83
|
-
f.geometry.coordinates = f.geometry.coordinates[0];
|
|
84
|
-
}
|
|
85
|
-
else
|
|
86
|
-
// TopoJSON does not guarantee proper winding order which messes up later processing. Fix it.
|
|
87
|
-
P.featureRewind(f);
|
|
88
|
-
|
|
89
|
-
return f;
|
|
68
|
+
return P.featureRewind(f, { validateHoles: false } );
|
|
90
69
|
}
|
|
91
70
|
|
|
92
71
|
export function topoContiguity(topo: Topo): any
|