@dra2020/baseclient 1.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.
- package/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/all/all.d.ts +18 -0
- package/dist/baseclient.js +9567 -0
- package/dist/baseclient.js.map +1 -0
- package/dist/context/all.d.ts +1 -0
- package/dist/context/context.d.ts +13 -0
- package/dist/filterexpr/all.d.ts +1 -0
- package/dist/filterexpr/filterexpr.d.ts +64 -0
- package/dist/fsm/all.d.ts +1 -0
- package/dist/fsm/fsm.d.ts +118 -0
- package/dist/logabstract/all.d.ts +1 -0
- package/dist/logabstract/log.d.ts +26 -0
- package/dist/logclient/all.d.ts +1 -0
- package/dist/logclient/log.d.ts +6 -0
- package/dist/ot-editutil/all.d.ts +2 -0
- package/dist/ot-editutil/oteditutil.d.ts +14 -0
- package/dist/ot-editutil/otmaputil.d.ts +21 -0
- package/dist/ot-js/all.d.ts +9 -0
- package/dist/ot-js/otarray.d.ts +111 -0
- package/dist/ot-js/otclientengine.d.ts +38 -0
- package/dist/ot-js/otcomposite.d.ts +37 -0
- package/dist/ot-js/otcounter.d.ts +17 -0
- package/dist/ot-js/otengine.d.ts +22 -0
- package/dist/ot-js/otmap.d.ts +19 -0
- package/dist/ot-js/otserverengine.d.ts +38 -0
- package/dist/ot-js/otsession.d.ts +111 -0
- package/dist/ot-js/ottypes.d.ts +29 -0
- package/dist/poly/all.d.ts +15 -0
- package/dist/poly/blend.d.ts +1 -0
- package/dist/poly/boundbox.d.ts +16 -0
- package/dist/poly/cartesian.d.ts +5 -0
- package/dist/poly/graham-scan.d.ts +8 -0
- package/dist/poly/hash.d.ts +1 -0
- package/dist/poly/matrix.d.ts +24 -0
- package/dist/poly/minbound.d.ts +1 -0
- package/dist/poly/poly.d.ts +52 -0
- package/dist/poly/polybin.d.ts +5 -0
- package/dist/poly/polylabel.d.ts +7 -0
- package/dist/poly/polypack.d.ts +30 -0
- package/dist/poly/polyround.d.ts +1 -0
- package/dist/poly/polysimplify.d.ts +1 -0
- package/dist/poly/quad.d.ts +48 -0
- package/dist/poly/selfintersect.d.ts +1 -0
- package/dist/poly/shamos.d.ts +1 -0
- package/dist/poly/simplify.d.ts +2 -0
- package/dist/poly/topo.d.ts +46 -0
- package/dist/poly/union.d.ts +48 -0
- package/dist/util/all.d.ts +5 -0
- package/dist/util/bintrie.d.ts +93 -0
- package/dist/util/countedhash.d.ts +19 -0
- package/dist/util/gradient.d.ts +15 -0
- package/dist/util/indexedarray.d.ts +15 -0
- package/dist/util/util.d.ts +68 -0
- package/docs/context.md +2 -0
- package/docs/fsm.md +243 -0
- package/docs/logabstract.md +2 -0
- package/docs/logclient.md +2 -0
- package/docs/ot-editutil.md +2 -0
- package/docs/ot-js.md +95 -0
- package/docs/poly.md +103 -0
- package/docs/util.md +2 -0
- package/lib/all/all.ts +19 -0
- package/lib/context/all.ts +1 -0
- package/lib/context/context.ts +82 -0
- package/lib/filterexpr/all.ts +1 -0
- package/lib/filterexpr/filterexpr.ts +625 -0
- package/lib/fsm/all.ts +1 -0
- package/lib/fsm/fsm.ts +549 -0
- package/lib/logabstract/all.ts +1 -0
- package/lib/logabstract/log.ts +55 -0
- package/lib/logclient/all.ts +1 -0
- package/lib/logclient/log.ts +105 -0
- package/lib/ot-editutil/all.ts +2 -0
- package/lib/ot-editutil/oteditutil.ts +180 -0
- package/lib/ot-editutil/otmaputil.ts +209 -0
- package/lib/ot-js/all.ts +9 -0
- package/lib/ot-js/otarray.ts +1168 -0
- package/lib/ot-js/otclientengine.ts +327 -0
- package/lib/ot-js/otcomposite.ts +247 -0
- package/lib/ot-js/otcounter.ts +145 -0
- package/lib/ot-js/otengine.ts +71 -0
- package/lib/ot-js/otmap.ts +144 -0
- package/lib/ot-js/otserverengine.ts +329 -0
- package/lib/ot-js/otsession.ts +199 -0
- package/lib/ot-js/ottypes.ts +98 -0
- package/lib/poly/all.ts +15 -0
- package/lib/poly/blend.ts +27 -0
- package/lib/poly/boundbox.ts +102 -0
- package/lib/poly/cartesian.ts +130 -0
- package/lib/poly/graham-scan.ts +401 -0
- package/lib/poly/hash.ts +15 -0
- package/lib/poly/matrix.ts +309 -0
- package/lib/poly/minbound.ts +211 -0
- package/lib/poly/poly.ts +767 -0
- package/lib/poly/polybin.ts +218 -0
- package/lib/poly/polylabel.ts +204 -0
- package/lib/poly/polypack.ts +458 -0
- package/lib/poly/polyround.ts +30 -0
- package/lib/poly/polysimplify.ts +24 -0
- package/lib/poly/quad.ts +272 -0
- package/lib/poly/selfintersect.ts +87 -0
- package/lib/poly/shamos.ts +297 -0
- package/lib/poly/simplify.ts +119 -0
- package/lib/poly/topo.ts +525 -0
- package/lib/poly/union.ts +371 -0
- package/lib/util/all.ts +5 -0
- package/lib/util/bintrie.ts +603 -0
- package/lib/util/countedhash.ts +83 -0
- package/lib/util/gradient.ts +108 -0
- package/lib/util/indexedarray.ts +80 -0
- package/lib/util/util.ts +695 -0
- package/package.json +52 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// Errors
|
|
2
|
+
export const ESuccess: number = 0; // Generic success
|
|
3
|
+
export const EFail: number = 1; // Generic failure
|
|
4
|
+
export const EFull: number = 2; // Too many clients attached to this session; try later
|
|
5
|
+
export const ERetry: number = 3; // Try request again
|
|
6
|
+
export const ENoSession: number = 4; // No record of such session
|
|
7
|
+
export const EClockSeen: number = 5;
|
|
8
|
+
export const EClockFailure: number = 6;
|
|
9
|
+
export const EClockReset: number = 7;
|
|
10
|
+
export const ENoUser: number = 8; // No user specified (internal error since all authorized reqs have user)
|
|
11
|
+
export const EBadRequest: number = 9; // Badly formed request
|
|
12
|
+
export const ELoadFailed: number = 10; // Session exists in index, but load of state failed ((temp?) internal error)
|
|
13
|
+
export const ENoPerm: number = 11; // No permissions (readonly)
|
|
14
|
+
export const ENoAccess: number = 12; // No access (no read)
|
|
15
|
+
export const EMaintenance: number = 13; // Server under maintenance
|
|
16
|
+
export const EClockAnomaly: number = 14; // Typically server restart and client is ahead
|
|
17
|
+
|
|
18
|
+
// Filters
|
|
19
|
+
export const FilterMyMaps: number = 0;
|
|
20
|
+
export const FilterSharedWithMe: number = 1;
|
|
21
|
+
export const FilterMyPublic: number = 2;
|
|
22
|
+
export const FilterRecent: number = 3;
|
|
23
|
+
export const FilterTrash: number = 4;
|
|
24
|
+
export const FilterPublic: number = 5;
|
|
25
|
+
export const FilterOfficial: number = 6;
|
|
26
|
+
export const FilterCount: number = 7;
|
|
27
|
+
export type Filter = number;
|
|
28
|
+
|
|
29
|
+
// Permissions
|
|
30
|
+
export const PermNone: number = 0; // No permissions
|
|
31
|
+
export const PermRead: number = 1; // Can view
|
|
32
|
+
export const PermWrite: number = 2; // Can modify
|
|
33
|
+
export const PermOwner: number = 4; // Can change deleted, published, access permissions
|
|
34
|
+
export const PermAdmin: number = 8; // Can administer site
|
|
35
|
+
export const PermEdit: number = (PermWrite|PermOwner);
|
|
36
|
+
export const PermAll: number = (PermRead|PermWrite|PermOwner|PermAdmin);
|
|
37
|
+
export type Permission = number;
|
|
38
|
+
|
|
39
|
+
// Server State
|
|
40
|
+
export const ServerStateRunning: number = 0;
|
|
41
|
+
export const ServerStateMaintenance: number = 1;
|
|
42
|
+
export type ServerState = number;
|
|
43
|
+
|
|
44
|
+
// Maps Access Tokens (IDs) to SessionID
|
|
45
|
+
export interface AccessSessionMap
|
|
46
|
+
{
|
|
47
|
+
[key: string]: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Maps Access ID to { list of users, permission } (stored with session)
|
|
51
|
+
export interface AccessMap
|
|
52
|
+
{
|
|
53
|
+
[key: string]: Access;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface Access
|
|
57
|
+
{
|
|
58
|
+
userIDs: string[]; // empty implies "anyone"
|
|
59
|
+
perm: Permission;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface Revision
|
|
63
|
+
{
|
|
64
|
+
id: string;
|
|
65
|
+
modifyTime: any; // JSON date
|
|
66
|
+
label?: string; // optional label
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type RevisionList = Revision[];
|
|
70
|
+
|
|
71
|
+
export interface SessionUser
|
|
72
|
+
{
|
|
73
|
+
id: string;
|
|
74
|
+
name: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ActiveUser
|
|
78
|
+
{
|
|
79
|
+
id: string;
|
|
80
|
+
name: string;
|
|
81
|
+
active: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface SessionUserIndex
|
|
85
|
+
{
|
|
86
|
+
[key: string]: SessionUser;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface SessionUserList
|
|
90
|
+
{
|
|
91
|
+
[key: string]: number;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface SessionProps
|
|
95
|
+
{
|
|
96
|
+
id: string;
|
|
97
|
+
name: string;
|
|
98
|
+
type: string;
|
|
99
|
+
description: string;
|
|
100
|
+
labels: string[];
|
|
101
|
+
createdBy: string;
|
|
102
|
+
lastActive: any; // JSON date (should be string)
|
|
103
|
+
createTime: any; // JSON date (should be string)
|
|
104
|
+
modifyTime: any; // JSON date (should be string)
|
|
105
|
+
clientCount: number;
|
|
106
|
+
maxClients: number;
|
|
107
|
+
requestCount: number;
|
|
108
|
+
deleted: boolean;
|
|
109
|
+
published?: string;
|
|
110
|
+
official: boolean;
|
|
111
|
+
loadFailed: boolean;
|
|
112
|
+
accessMap: AccessMap;
|
|
113
|
+
revisions: RevisionList;
|
|
114
|
+
xprops?: { [prop: string]: string };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// True to add, False to remove
|
|
118
|
+
export type LabelUpdate = { [name: string]: boolean|null }
|
|
119
|
+
|
|
120
|
+
export interface SessionUpdateProps
|
|
121
|
+
{
|
|
122
|
+
id: string;
|
|
123
|
+
deleted?: boolean;
|
|
124
|
+
published?: boolean;
|
|
125
|
+
official?: boolean;
|
|
126
|
+
name?: string;
|
|
127
|
+
description?: string;
|
|
128
|
+
labelupdate?: LabelUpdate;
|
|
129
|
+
access?: Access;
|
|
130
|
+
accessUpdate?: AccessMap;
|
|
131
|
+
restore?: string; // Revision ID
|
|
132
|
+
revision?: Revision; // If ID is empty, snap a new revision, otherwise label it.
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface SessionsIndex
|
|
136
|
+
{
|
|
137
|
+
[key: string]: SessionProps;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// The semantics here are:
|
|
141
|
+
// if aid is provided, only look through that access token.
|
|
142
|
+
// if aid is missing, look for that user in any access token (but ignore anonymous tokens).
|
|
143
|
+
// in either case, return the full set of permissions a user has.
|
|
144
|
+
//
|
|
145
|
+
export function accessFindUser(a: Access, uid: string): Permission
|
|
146
|
+
{
|
|
147
|
+
if (a === undefined) return PermNone;
|
|
148
|
+
if (a.userIDs.length == 0)
|
|
149
|
+
return a.perm;
|
|
150
|
+
for (let i: number = 0; i < a.userIDs.length; i++)
|
|
151
|
+
if (a.userIDs[i] === uid) return a.perm;
|
|
152
|
+
return PermNone;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function accessMapFindUser(accessMap: AccessMap, uid: string, aid?: string): Permission
|
|
156
|
+
{
|
|
157
|
+
if (aid !== undefined)
|
|
158
|
+
return accessFindUser(accessMap[aid], uid);
|
|
159
|
+
else
|
|
160
|
+
{
|
|
161
|
+
let perm: Permission = PermNone;
|
|
162
|
+
for (var aid1 in accessMap) if (accessMap.hasOwnProperty(aid1))
|
|
163
|
+
{
|
|
164
|
+
let a = accessMap[aid1];
|
|
165
|
+
for (let i: number = 0; i < a.userIDs.length; i++)
|
|
166
|
+
if (a.userIDs[i] === uid)
|
|
167
|
+
{
|
|
168
|
+
perm |= a.perm;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return perm;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function SessionFilterFunction(p: SessionProps, uid: string, f: number): boolean
|
|
177
|
+
{
|
|
178
|
+
if (f === FilterPublic) return p.published !== undefined;
|
|
179
|
+
|
|
180
|
+
if (f === FilterSharedWithMe)
|
|
181
|
+
{
|
|
182
|
+
if (p.createdBy === uid) return false;
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// All other filters require ownership of this session
|
|
187
|
+
if (p.createdBy !== uid) return false;
|
|
188
|
+
|
|
189
|
+
if (f === FilterTrash) return p.deleted;
|
|
190
|
+
|
|
191
|
+
if (f === FilterRecent && !p.deleted && p.modifyTime)
|
|
192
|
+
{
|
|
193
|
+
let d = new Date();
|
|
194
|
+
let interval = d.getTime() - new Date(p.modifyTime).getTime();
|
|
195
|
+
return interval < (1000 * 60 * 60 * 24 * 7); // one week
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return !p.deleted;
|
|
199
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
|
|
2
|
+
// Resource data type that supports basic OT inteface.
|
|
3
|
+
export interface IOTResource
|
|
4
|
+
{
|
|
5
|
+
resourceName: string;
|
|
6
|
+
underlyingType: string;
|
|
7
|
+
edits: any[];
|
|
8
|
+
|
|
9
|
+
// Normally defined by base class
|
|
10
|
+
length: number;
|
|
11
|
+
empty(): void;
|
|
12
|
+
isEmpty(): boolean;
|
|
13
|
+
|
|
14
|
+
// Copy an instance
|
|
15
|
+
copy(): IOTResource;
|
|
16
|
+
|
|
17
|
+
// Test whether two operations are effectively equivalent
|
|
18
|
+
effectivelyEqual(rhs: IOTResource): boolean;
|
|
19
|
+
|
|
20
|
+
// Core OT algorithm for this type
|
|
21
|
+
transform(rhs: IOTResource, bPriorIsService: boolean): void; // throws on error
|
|
22
|
+
|
|
23
|
+
// compose two edit actions
|
|
24
|
+
compose(rhs: IOTResource): void; // throws on error
|
|
25
|
+
|
|
26
|
+
// apply this edit to an existing value, returning new value (if underlying type is mutable, may modify input)
|
|
27
|
+
apply(startValue: any): any;
|
|
28
|
+
|
|
29
|
+
// return a collapsed, minimal version of the operation, suitable for constructing from scratch from empty initial value
|
|
30
|
+
minimize(): void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Useful base class
|
|
34
|
+
export class OTResourceBase implements IOTResource
|
|
35
|
+
{
|
|
36
|
+
resourceName: string;
|
|
37
|
+
underlyingType: string;
|
|
38
|
+
edits: any[];
|
|
39
|
+
|
|
40
|
+
constructor(rname: string, utype: string)
|
|
41
|
+
{
|
|
42
|
+
this.resourceName = rname;
|
|
43
|
+
this.underlyingType = utype;
|
|
44
|
+
this.edits = [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
get length(): number
|
|
48
|
+
{
|
|
49
|
+
return this.edits.length;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Set an existing instance of the operation to be empty
|
|
53
|
+
empty(): void
|
|
54
|
+
{
|
|
55
|
+
this.edits = [];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Test
|
|
59
|
+
isEmpty(): boolean
|
|
60
|
+
{
|
|
61
|
+
return this.edits.length == 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Copy an instance
|
|
65
|
+
copy(): OTResourceBase
|
|
66
|
+
{
|
|
67
|
+
throw "OTResourceBase.copy must be overridden in subclass";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Test whether two operations are effectively equivalent
|
|
71
|
+
effectivelyEqual(rhs: OTResourceBase): boolean
|
|
72
|
+
{
|
|
73
|
+
throw "OTResourceBase.effectivelyEqual must be overridden in subclass";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Core OT algorithm for this type
|
|
77
|
+
transform(rhs: OTResourceBase, bPriorIsService: boolean): void
|
|
78
|
+
{
|
|
79
|
+
throw "OTResourceBase.transform must be overridden in subclass";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// compose two edit actions
|
|
83
|
+
compose(rhs: OTResourceBase): void
|
|
84
|
+
{
|
|
85
|
+
throw "OTResourceBase.compose must be overridden in subclass";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// apply this edit to an existing value, returning new value (if underlying type is mutable, may modify input)
|
|
89
|
+
apply(startValue: any): any
|
|
90
|
+
{
|
|
91
|
+
throw "OTResourceBase.apply must be overridden in subclass";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
minimize(): void
|
|
95
|
+
{
|
|
96
|
+
// Default implementation does nothing.
|
|
97
|
+
}
|
|
98
|
+
}
|
package/lib/poly/all.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from './poly';
|
|
2
|
+
export * from './union';
|
|
3
|
+
export * from './quad';
|
|
4
|
+
export * from './polylabel';
|
|
5
|
+
export * from './polysimplify';
|
|
6
|
+
export * from './polypack';
|
|
7
|
+
export * from './polybin';
|
|
8
|
+
export * from './boundbox';
|
|
9
|
+
export * from './blend';
|
|
10
|
+
export * from './cartesian';
|
|
11
|
+
export * from './minbound';
|
|
12
|
+
export * from './polyround';
|
|
13
|
+
export * from './topo';
|
|
14
|
+
export * from './selfintersect';
|
|
15
|
+
export * from './shamos';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as Util from '../util/all';
|
|
2
|
+
import * as PP from './polypack';
|
|
3
|
+
|
|
4
|
+
// Takes array of polygon or multi-polygon coordinate structures and returns a single multi-polygon
|
|
5
|
+
// structure. The coordinates may be passed in using packed format.
|
|
6
|
+
|
|
7
|
+
export function blend(polys: any[]): any
|
|
8
|
+
{
|
|
9
|
+
if (polys == null || polys.length == 0) return null;
|
|
10
|
+
|
|
11
|
+
let result: any[] = [];
|
|
12
|
+
|
|
13
|
+
for (let i: number = 0; i < polys.length; i++)
|
|
14
|
+
{
|
|
15
|
+
let p = PP.polyUnpack(polys[i]);
|
|
16
|
+
let d = Util.depthof(p);
|
|
17
|
+
if (d === 4)
|
|
18
|
+
result.push(p);
|
|
19
|
+
else if (d === 5)
|
|
20
|
+
for (let j: number = 0; j < p.length; j++)
|
|
21
|
+
result.push(p[j]);
|
|
22
|
+
else if (p.length === undefined || p.length > 0)
|
|
23
|
+
throw new Error('blend expects polygon or multi-polygon coordinates');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import * as Util from '../util/all';
|
|
2
|
+
|
|
3
|
+
import * as P from './poly';
|
|
4
|
+
import * as PP from './polypack';
|
|
5
|
+
|
|
6
|
+
// Note, in Geo format so top > bottom
|
|
7
|
+
export interface BoundBox
|
|
8
|
+
{
|
|
9
|
+
left?: number;
|
|
10
|
+
top?: number;
|
|
11
|
+
right?: number;
|
|
12
|
+
bottom?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function boundboxWidth(bb: BoundBox): number { return Math.abs(bb.right - bb.left); }
|
|
16
|
+
export function boundboxHeight(bb: BoundBox): number { return Math.abs(bb.bottom - bb.top); }
|
|
17
|
+
|
|
18
|
+
export function boundboxCX(bb: BoundBox): number { return bb.left + (bb.right - bb.left) / 2; }
|
|
19
|
+
export function boundboxCY(bb: BoundBox): number { return bb.top + (bb.bottom - bb.top) / 2; }
|
|
20
|
+
|
|
21
|
+
export function clipLon(lon: number): number { return lon <= 0 ? lon : -179; }
|
|
22
|
+
|
|
23
|
+
export function boundboxExtend(bbox: BoundBox, x: number, y: number): void
|
|
24
|
+
{
|
|
25
|
+
x = clipLon(x);
|
|
26
|
+
if (bbox.left === undefined || x < bbox.left)
|
|
27
|
+
bbox.left = x;
|
|
28
|
+
if (bbox.right === undefined || x > bbox.right)
|
|
29
|
+
bbox.right = x;
|
|
30
|
+
if (bbox.top === undefined || y > bbox.top)
|
|
31
|
+
bbox.top = y;
|
|
32
|
+
if (bbox.bottom === undefined || y < bbox.bottom)
|
|
33
|
+
bbox.bottom = y;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function boundboxExtendPacked(pp: PP.PolyPack, bbox: BoundBox): void
|
|
37
|
+
{
|
|
38
|
+
let buffer = pp.buffer as Float64Array;
|
|
39
|
+
let offset = pp.buffer[pp.offset+1] + pp.offset;
|
|
40
|
+
let end = pp.offset + pp.length;
|
|
41
|
+
for (; offset < end; offset += 2)
|
|
42
|
+
boundboxExtend(bbox, buffer[offset], buffer[offset+1]);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function boundbox(poly: any, bbox?: BoundBox): BoundBox
|
|
46
|
+
{
|
|
47
|
+
let i: number;
|
|
48
|
+
|
|
49
|
+
if (bbox === undefined)
|
|
50
|
+
bbox = { };
|
|
51
|
+
|
|
52
|
+
if (poly)
|
|
53
|
+
{
|
|
54
|
+
// Collection
|
|
55
|
+
if (poly.features)
|
|
56
|
+
{
|
|
57
|
+
for (i = 0; i < poly.features.length; i++)
|
|
58
|
+
boundbox(poly.features[i], bbox);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// feature
|
|
62
|
+
else if (poly.geometry)
|
|
63
|
+
{
|
|
64
|
+
if (poly.geometry.packed)
|
|
65
|
+
boundboxExtendPacked(poly.geometry.packed as PP.PolyPack, bbox);
|
|
66
|
+
else if (poly.geometry.coordinates)
|
|
67
|
+
boundbox(poly.geometry.coordinates, bbox);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// raw packed buffer
|
|
71
|
+
else if (poly.offset !== undefined)
|
|
72
|
+
boundboxExtendPacked(poly as PP.PolyPack, bbox);
|
|
73
|
+
|
|
74
|
+
// array of other things (like raw coordinates, or other aggregates)
|
|
75
|
+
else if (Array.isArray(poly) && typeof poly[0] !== 'number')
|
|
76
|
+
{
|
|
77
|
+
for (i = 0; i < poly.length; i++)
|
|
78
|
+
boundbox(poly[i], bbox);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// single point
|
|
82
|
+
else
|
|
83
|
+
boundboxExtend(bbox, poly[0], poly[1]);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return bbox;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function boundboxPoly(bb: BoundBox): any
|
|
90
|
+
{
|
|
91
|
+
return [ [ [bb.left, bb.top], [bb.left, bb.bottom], [bb.right, bb.bottom], [bb.right, bb.top], [bb.left, bb.top] ] ];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function boundboxArea(poly: any): number
|
|
95
|
+
{
|
|
96
|
+
return P.polyArea(boundboxPoly(boundbox(poly)));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function boundboxIntersects(bb1: BoundBox, bb2: BoundBox): boolean
|
|
100
|
+
{
|
|
101
|
+
return !(bb1.left > bb2.right || bb1.right < bb2.left || bb1.top < bb2.bottom || bb1.bottom > bb2.top);
|
|
102
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
//
|
|
2
|
+
// GEO-FEATURES UTILITIES
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as GeoJSON from 'geojson';
|
|
6
|
+
import * as P from './poly';
|
|
7
|
+
import * as PP from './polypack';
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
// HELPER
|
|
11
|
+
|
|
12
|
+
function bufferToRing(b: Float64Array, s: number, nPoints: number): any
|
|
13
|
+
{
|
|
14
|
+
let r: any = [];
|
|
15
|
+
let e = s + nPoints * 2;
|
|
16
|
+
for (; s < e; s += 2)
|
|
17
|
+
r.push([ b[s], b[s+1] ]);
|
|
18
|
+
return r;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function polyParts(poly: any): GeoJSON.FeatureCollection
|
|
22
|
+
{
|
|
23
|
+
let pp = P.polyNormalize(poly);
|
|
24
|
+
|
|
25
|
+
let parts: GeoJSON.FeatureCollection = { type: 'FeatureCollection', features: [] };
|
|
26
|
+
let af: any = parts.features;
|
|
27
|
+
let f: any;
|
|
28
|
+
PP.polyPackEachRing(pp, (b: Float64Array, iPoly: number, iRing: number, iOffset: number, nPoints: number) => {
|
|
29
|
+
let r = bufferToRing(b, iOffset, nPoints);
|
|
30
|
+
if (iRing == 0)
|
|
31
|
+
{
|
|
32
|
+
f = { type: 'Feature',
|
|
33
|
+
properties: { id: String(iPoly+1) },
|
|
34
|
+
geometry: { type: 'Polygon', coordinates: [ r ] } };
|
|
35
|
+
af.push(f);
|
|
36
|
+
}
|
|
37
|
+
else
|
|
38
|
+
f.geometry.coordinates.push(r);
|
|
39
|
+
});
|
|
40
|
+
return parts;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// CARTESIAN ("FLAT") AREA, PERIMETER, AND DIAMETER
|
|
44
|
+
|
|
45
|
+
export function polyAreaFlat(poly: any): number
|
|
46
|
+
{
|
|
47
|
+
let pp = P.polyNormalize(poly);
|
|
48
|
+
|
|
49
|
+
let a: number = 0;
|
|
50
|
+
PP.polyPackEachRing(pp, (b: Float64Array, iPoly: number, iRing: number, iOffset: number, nPoints: number) => {
|
|
51
|
+
a += polySimpleAreaFlat(b, iOffset, nPoints) * (iRing == 0 ? 1 : -1);
|
|
52
|
+
});
|
|
53
|
+
return a;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Algorithm for the area of a simple/single planar polygon:
|
|
57
|
+
// https://algorithmtutor.com/Computational-Geometry/Area-of-a-polygon-given-a-set-of-points/
|
|
58
|
+
// https://mathopenref.com/coordpolygonarea2.html
|
|
59
|
+
//
|
|
60
|
+
// function polygonArea(X, Y, numPoints)
|
|
61
|
+
// {
|
|
62
|
+
// area = 0; // Accumulates area in the loop
|
|
63
|
+
// j = numPoints-1; // The last vertex is the 'previous' one to the first
|
|
64
|
+
|
|
65
|
+
// for (i=0; i<numPoints; i++)
|
|
66
|
+
// { area = area + (X[j]+X[i]) * (Y[j]-Y[i]);
|
|
67
|
+
// j = i; //j is previous vertex to i
|
|
68
|
+
// }
|
|
69
|
+
// return area/2;
|
|
70
|
+
// }
|
|
71
|
+
|
|
72
|
+
// Reimplemented to use polygons vs. X & Y vectors
|
|
73
|
+
function polySimpleAreaFlat(b: Float64Array, i: number, nPoints: number): number
|
|
74
|
+
{
|
|
75
|
+
let a = 0;
|
|
76
|
+
let e = i + nPoints * 2;
|
|
77
|
+
let j = e - 2; // The last vertex is the 'previous' one to the first
|
|
78
|
+
|
|
79
|
+
for (; i < e; i += 2)
|
|
80
|
+
{
|
|
81
|
+
a += (b[j] + b[i]) * (b[j+1] - b[i+1]);
|
|
82
|
+
j = i;
|
|
83
|
+
}
|
|
84
|
+
return Math.abs(a / 2);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// You would need to divide by Poly.EARTH_RADIUS to go from the returned units of meters to Lat/Lon “units.”
|
|
88
|
+
// NOTE - No conversion of degrees to radians!
|
|
89
|
+
|
|
90
|
+
export function polyPerimeterFlat(poly: any): number
|
|
91
|
+
{
|
|
92
|
+
let pp = P.polyNormalize(poly);
|
|
93
|
+
|
|
94
|
+
let perimeter: number = 0;
|
|
95
|
+
PP.polyPackEachRing(pp, (b: Float64Array, iPoly: number, iRing: number, iOffset: number, nPoints: number) => {
|
|
96
|
+
// Ignore holes so only look at first ring
|
|
97
|
+
if (iRing == 0)
|
|
98
|
+
{
|
|
99
|
+
let s = iOffset;
|
|
100
|
+
let e = s + (nPoints-1) * 2; // index *at* last point since we look at next point in each iteration
|
|
101
|
+
for (; s < e; s += 2)
|
|
102
|
+
perimeter += _distance(b[s], b[s+1], b[s+2], b[s+3]);
|
|
103
|
+
s = iOffset;
|
|
104
|
+
if (nPoints > 2 && (b[s] != b[e] || b[s+1] != b[e+1]))
|
|
105
|
+
perimeter += _distance(b[s], b[s+1], b[e], b[e+1]);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return perimeter;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function _distance(x1: number, y1: number, x2: number, y2: number): number
|
|
113
|
+
{
|
|
114
|
+
const dLat = y2 - y1;
|
|
115
|
+
const dLon = x2 - x1;
|
|
116
|
+
let d: number;
|
|
117
|
+
|
|
118
|
+
d = Math.sqrt((dLat * dLat) + (dLon * dLon));
|
|
119
|
+
|
|
120
|
+
return d;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// The polyCircle code is already just treating the coordinate system as Cartesian.
|
|
124
|
+
// So just compute circle and return diameter.
|
|
125
|
+
|
|
126
|
+
export function polyDiameterFlat(poly: any): number
|
|
127
|
+
{
|
|
128
|
+
let circle = P.polyToCircle(poly);
|
|
129
|
+
return circle ? circle.r * 2 : 0;
|
|
130
|
+
}
|