@pirireis/webglobeplugins 0.6.17 → 0.6.18
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/bearing-line/plugin.js +104 -58
- package/bearing-line/pluginOLD.js +430 -0
- package/package.json +1 -1
- package/pin/pin-point-totem.js +77 -0
- package/programs/line-on-globe/circle-accurate.js +1 -1
- package/programs/line-on-globe/naive-accurate.js +8 -5
- package/programs/rings/partial-ring/piece-of-pie.js +315 -0
- package/util/check/get.js +1 -1
- package/util/shaderfunctions/geometrytransformations.js +4 -3
package/bearing-line/plugin.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { LineOnGlobeCache } from '../programs/line-on-globe/naive';
|
|
3
|
-
import { CircleCache } from '../programs/line-on-globe/circle';
|
|
1
|
+
import { pieceOfPieProgramCache } from '../programs/rings/partial-ring/piece-of-pie';
|
|
2
|
+
import { LineOnGlobeCache } from '../programs/line-on-globe/naive-accurate';
|
|
3
|
+
import { CircleCache } from '../programs/line-on-globe/circle-accurate';
|
|
4
4
|
import { BufferOrchestrator, BufferManager } from '../util/account';
|
|
5
|
-
import { AngledLineProgramCache } from '../programs/line-on-globe/angled-line';
|
|
5
|
+
import { AngledLineProgramCache } from '../programs/line-on-globe/angled-line'; // TODO calculate the bearing target for 2d and 3d and use lineOnGlobeProgram
|
|
6
6
|
import { mapGetOrThrow } from "../util/check/get";
|
|
7
7
|
import { ContextTextWriter } from '../write-text/context-text'
|
|
8
8
|
export const RINGPARTIAL_DRAW_MODE = Object.freeze({
|
|
@@ -15,7 +15,10 @@ const constraintFloat = (x, lowerBound, upperBound) => {
|
|
|
15
15
|
if (lowerBound > x || x > upperBound) throw new Error(`input must be between ${lowerBound} - ${upperBound}`)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* partial ring angle should be different on globe and mercator.
|
|
20
|
+
*
|
|
21
|
+
*/
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
/**
|
|
@@ -107,7 +110,7 @@ export default class Plugin {
|
|
|
107
110
|
this.gl = gl;
|
|
108
111
|
this.globe = globe;
|
|
109
112
|
this.lineProgram = LineOnGlobeCache.get(globe);
|
|
110
|
-
this.ringProgram =
|
|
113
|
+
this.ringProgram = pieceOfPieProgramCache.get(globe);
|
|
111
114
|
this.angledLineProgram = AngledLineProgramCache.get(globe);
|
|
112
115
|
this.circleProgram = CircleCache.get(globe);
|
|
113
116
|
// this.angleTextContext = new ContextTextWriter(globe);
|
|
@@ -118,13 +121,30 @@ export default class Plugin {
|
|
|
118
121
|
const initialCapacity = this.bufferOrchestrator.capacity;
|
|
119
122
|
this.bufferManagersCompMap = new Map(
|
|
120
123
|
[
|
|
121
|
-
|
|
124
|
+
|
|
125
|
+
["centerCoords2d", {
|
|
126
|
+
'bufferManager': new BufferManager(gl, 2, { bufferType, initialCapacity }),
|
|
127
|
+
'adaptor': (item) => new Float32Array(globe.api_GetMercator2DPoint(item.long, item.lat)),
|
|
128
|
+
}],
|
|
129
|
+
["centerCoords3d", {
|
|
130
|
+
'bufferManager': new BufferManager(gl, 3, { bufferType, initialCapacity }),
|
|
131
|
+
'adaptor': (item) => new Float32Array(globe.api_GetCartesian3DPoint(item.long, item.lat, 0, 0)),
|
|
132
|
+
}],
|
|
133
|
+
["targetCoords2d", {
|
|
122
134
|
'bufferManager': new BufferManager(gl, 2, { bufferType, initialCapacity }),
|
|
123
|
-
'adaptor': (item) => new Float32Array(
|
|
135
|
+
'adaptor': (item) => new Float32Array(globe.api_GetMercator2DPoint(item.endLong, item.endLat))
|
|
136
|
+
}],
|
|
137
|
+
["targetCoords3d", {
|
|
138
|
+
'bufferManager': new BufferManager(gl, 3, { bufferType, initialCapacity }),
|
|
139
|
+
'adaptor': (item) => new Float32Array(globe.api_GetCartesian3DPoint(item.endLong, item.endLat, 0, 0))
|
|
124
140
|
}],
|
|
125
|
-
["
|
|
141
|
+
["bearingTargetCoords2d", {
|
|
126
142
|
'bufferManager': new BufferManager(gl, 2, { bufferType, initialCapacity }),
|
|
127
|
-
'adaptor': (item) => new Float32Array(
|
|
143
|
+
'adaptor': (item) => new Float32Array(globe.api_GetMercator2DPoint(item.bearingLong, item.bearingLat))
|
|
144
|
+
}],
|
|
145
|
+
["bearingTargetCoords3d", {
|
|
146
|
+
'bufferManager': new BufferManager(gl, 3, { bufferType, initialCapacity }),
|
|
147
|
+
'adaptor': (item) => new Float32Array(globe.api_GetCartesian3DPoint(item.bearingLong, item.bearingLat, 0, 0))
|
|
128
148
|
}],
|
|
129
149
|
["startAngle", {
|
|
130
150
|
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
@@ -134,6 +154,26 @@ export default class Plugin {
|
|
|
134
154
|
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
135
155
|
'adaptor': (item) => new Float32Array([item.tailAngle])
|
|
136
156
|
}],
|
|
157
|
+
["startAngle2d", {
|
|
158
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
159
|
+
'adaptor': (item) => new Float32Array([item.startAngle2d])
|
|
160
|
+
}],
|
|
161
|
+
["tailAngle2d", {
|
|
162
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
163
|
+
'adaptor': (item) => new Float32Array([item.tailAngle2d])
|
|
164
|
+
}],
|
|
165
|
+
["startAngle3d", {
|
|
166
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
167
|
+
'adaptor': (item) => new Float32Array([item.startAngle3d])
|
|
168
|
+
}],
|
|
169
|
+
["tailAngle3d", {
|
|
170
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
171
|
+
'adaptor': (item) => new Float32Array([item.tailAngle3d])
|
|
172
|
+
}],
|
|
173
|
+
["bearingDashRatio", {
|
|
174
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
175
|
+
'adaptor': (item) => new Float32Array([0])
|
|
176
|
+
}],
|
|
137
177
|
["rgba", {
|
|
138
178
|
'bufferManager': new BufferManager(gl, 4, { bufferType, initialCapacity }),
|
|
139
179
|
'adaptor': (item) => new Float32Array(item.rgba)
|
|
@@ -173,38 +213,42 @@ export default class Plugin {
|
|
|
173
213
|
|
|
174
214
|
const obj = function (bufferManagerComp) {
|
|
175
215
|
return { 'buffer': bufferManagerComp.bufferManager.buffer, 'stride': 0, 'offset': 0 }
|
|
216
|
+
|
|
176
217
|
};
|
|
177
218
|
|
|
178
219
|
this.lineVao = this.lineProgram.createVAO(
|
|
179
|
-
...['
|
|
220
|
+
...['centerCoords2d', 'centerCoords3d', 'targetCoords2d', 'targetCoords3d', 'dashRatio', 'dashOpacity', 'rgba'].map(key => obj(this.bufferManagersCompMap.get(key))));
|
|
180
221
|
this.ringVao = this.ringProgram.createVAO(
|
|
181
|
-
...['
|
|
222
|
+
...['centerCoords2d', 'centerCoords3d',
|
|
223
|
+
'startAngle2d',
|
|
224
|
+
'tailAngle2d',
|
|
225
|
+
'startAngle3d',
|
|
226
|
+
'tailAngle3d',
|
|
227
|
+
'rgba',
|
|
228
|
+
'radius',
|
|
229
|
+
'rgbaMode'].map(key => obj(this.bufferManagersCompMap.get(key))));
|
|
182
230
|
{
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const colorBuffer = this.bufferManagersCompMap.get("rgba");
|
|
186
|
-
angledLineBuffers.push(
|
|
187
|
-
{ 'buffer': colorBuffer.bufferManager.buffer, 'stride': 16, 'offset': 12 },
|
|
188
|
-
)
|
|
189
|
-
this.angledLineVao = this.angledLineProgram.createVAO(...angledLineBuffers);
|
|
231
|
+
this.bearingLineVAO = this.lineProgram.createVAO(
|
|
232
|
+
...['centerCoords2d', 'centerCoords3d', 'bearingTargetCoords2d', 'bearingTargetCoords3d', 'bearingDashRatio', 'dashOpacity', 'rgba'].map(key => obj(this.bufferManagersCompMap.get(key))));
|
|
190
233
|
}
|
|
191
234
|
// centerObj, startAngleObj, radiusObj, colorObj, dashRatioObj, dashOpacityObj
|
|
192
235
|
this.circleVao = this.circleProgram.createVAO(
|
|
193
|
-
...["
|
|
236
|
+
...["centerCoords2d", "centerCoords3d", "bigRadius", "rgba", "circleDashAngle", "dashOpacity"].map(key => obj(this.bufferManagersCompMap.get(key))));
|
|
194
237
|
}
|
|
195
238
|
|
|
196
239
|
|
|
197
240
|
|
|
198
241
|
draw3D() {
|
|
199
242
|
const { gl } = this;
|
|
200
|
-
this.lineProgram.draw(this.lineVao, this.bufferOrchestrator.length, this._opacity);
|
|
201
243
|
gl.disable(gl.DEPTH_TEST);
|
|
244
|
+
|
|
245
|
+
this.lineProgram.draw(this.lineVao, this.bufferOrchestrator.length, this._opacity);
|
|
202
246
|
if (this.drawAngleRing) {
|
|
203
247
|
this.ringProgram.draw(this.bufferOrchestrator.length, this.ringVao, 360, this._opacity * 0.8, RINGPARTIAL_DRAW_MODE.TRIANGLE_FAN);
|
|
204
248
|
this.ringProgram.draw(this.bufferOrchestrator.length, this.ringVao, 360, this._opacity, RINGPARTIAL_DRAW_MODE.LINE_STRIP);
|
|
205
249
|
}
|
|
206
250
|
if (this.drawBearingLine) {
|
|
207
|
-
this.
|
|
251
|
+
this.lineProgram.draw(this.bearingLineVAO, this.bufferOrchestrator.length, this._opacity * 0.8);
|
|
208
252
|
}
|
|
209
253
|
if (this.drawVRM) {
|
|
210
254
|
this.circleProgram.draw(this.circleVao, this.bufferOrchestrator.length, this._opacity);
|
|
@@ -254,7 +298,7 @@ export default class Plugin {
|
|
|
254
298
|
|
|
255
299
|
|
|
256
300
|
deleteBulk(keys) {
|
|
257
|
-
this.bufferOrchestrator.deleteBulk(keys, this.bufferManagersCompMap
|
|
301
|
+
this.bufferOrchestrator.deleteBulk(keys, this.bufferManagersCompMap);
|
|
258
302
|
this._deleteTexts(keys);
|
|
259
303
|
this.globe.DrawRender();
|
|
260
304
|
}
|
|
@@ -278,7 +322,9 @@ export default class Plugin {
|
|
|
278
322
|
data.push(this.__updateCoordsAdaptor(item));
|
|
279
323
|
}
|
|
280
324
|
|
|
281
|
-
bufferOrchestrator.updateBulk(data, bufferManagersCompMap, ["
|
|
325
|
+
bufferOrchestrator.updateBulk(data, bufferManagersCompMap, ["centerCoords2d", "centerCoords3d", "targetCoords2d", "targetCoords3d", "startAngle", "tailAngle",
|
|
326
|
+
"startAngle2d", "tailAngle2d", "startAngle3d", "tailAngle3d", "bearingTargetCoords2d", "bearingTargetCoords3d",
|
|
327
|
+
"bearingAngle", "bigRadius", "radius"]);
|
|
282
328
|
globe.DrawRender();
|
|
283
329
|
}
|
|
284
330
|
|
|
@@ -310,34 +356,17 @@ export default class Plugin {
|
|
|
310
356
|
|
|
311
357
|
|
|
312
358
|
__insertAdaptor(item) {
|
|
313
|
-
|
|
314
|
-
const long = radian(item.long)
|
|
315
|
-
const endLat = radian(item.endLat)
|
|
316
|
-
const endLong = radian(item.endLong)
|
|
359
|
+
|
|
317
360
|
const rgba = item.rgba !== undefined ? item.rgba : [0, 0, 0, 0];
|
|
318
361
|
const rgbaMode = item.rgbaMode !== undefined ? item.rgbaMode : 0;
|
|
319
362
|
const dashRatio = item.dashRatio !== undefined ? item.dashRatio : 1.0;
|
|
320
363
|
const dashOpacity = item.dashOpacity !== undefined ? item.dashOpacity : 0.9;
|
|
321
364
|
const circleDashAngle = item.circleDashAngle !== undefined ? item.circleDashAngle : 360;
|
|
322
|
-
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
const bearingAngle = radian(item.bearingAngle - 90);
|
|
326
|
-
let tailAngle = bearingAngle - startAngle;
|
|
327
|
-
if (tailAngle > 0) {
|
|
328
|
-
tailAngle -= Math.PI * 2;
|
|
329
|
-
}
|
|
365
|
+
|
|
366
|
+
const coordsData = this.__updateCoordsAdaptor(item);
|
|
367
|
+
|
|
330
368
|
return {
|
|
331
|
-
|
|
332
|
-
lat,
|
|
333
|
-
long,
|
|
334
|
-
endLat,
|
|
335
|
-
endLong,
|
|
336
|
-
bearingAngle,
|
|
337
|
-
radius,
|
|
338
|
-
bigRadius,
|
|
339
|
-
startAngle,
|
|
340
|
-
tailAngle,
|
|
369
|
+
...coordsData,
|
|
341
370
|
rgba,
|
|
342
371
|
dashRatio,
|
|
343
372
|
dashOpacity,
|
|
@@ -353,27 +382,44 @@ export default class Plugin {
|
|
|
353
382
|
const endLong = radian(item.endLong)
|
|
354
383
|
const bigRadius = item.bigRadius !== undefined ? item.bigRadius : this.globe.Math.GetDist3D(item.long, item.lat, item.endLong, item.endLat);
|
|
355
384
|
const radius = item.radius !== undefined ? item.radius : bigRadius * 0.2;
|
|
356
|
-
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
const { long: bearingLong, lat: bearingLat } = this.globe.Math.FindPointByPolar(item.long, item.lat, bigRadius, item.bearingAngle)
|
|
388
|
+
|
|
389
|
+
const startAngle2d = calculateStartAngle(long, lat, endLong, endLat);
|
|
390
|
+
const bearingAngle2d = calculateStartAngle(long, lat, radian(bearingLong), radian(bearingLat))
|
|
391
|
+
let tailAngle2d = bearingAngle2d - startAngle2d;
|
|
392
|
+
if (tailAngle2d > 0) {
|
|
393
|
+
tailAngle2d -= Math.PI * 2;
|
|
394
|
+
}
|
|
395
|
+
|
|
357
396
|
const bearingAngle = radian(item.bearingAngle - 90);
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
397
|
+
const startAngle3d = radian(this.globe.Math.GetAzimuthAngle(item.long, item.lat, item.endLong, item.endLat)) - radian(90);
|
|
398
|
+
let tailAngle3d = bearingAngle - startAngle3d;
|
|
399
|
+
if (tailAngle3d > 0) {
|
|
400
|
+
tailAngle3d -= Math.PI * 2;
|
|
361
401
|
}
|
|
402
|
+
|
|
362
403
|
return {
|
|
363
404
|
key: item.key,
|
|
364
|
-
lat,
|
|
365
|
-
long,
|
|
366
|
-
endLat,
|
|
367
|
-
endLong,
|
|
405
|
+
lat: item.lat,
|
|
406
|
+
long: item.long,
|
|
407
|
+
endLat: item.endLat,
|
|
408
|
+
endLong: item.endLong,
|
|
368
409
|
bearingAngle,
|
|
369
410
|
radius,
|
|
370
411
|
bigRadius,
|
|
371
|
-
|
|
372
|
-
|
|
412
|
+
startAngle2d,
|
|
413
|
+
tailAngle2d,
|
|
414
|
+
startAngle3d,
|
|
415
|
+
tailAngle3d,
|
|
416
|
+
bearingLong,
|
|
417
|
+
bearingLat
|
|
373
418
|
};
|
|
374
419
|
}
|
|
375
420
|
|
|
376
421
|
|
|
422
|
+
|
|
377
423
|
//TODO free
|
|
378
424
|
free() {
|
|
379
425
|
if (this.isFreed) return;
|
|
@@ -381,7 +427,7 @@ export default class Plugin {
|
|
|
381
427
|
bufferManager.free();
|
|
382
428
|
});
|
|
383
429
|
LineOnGlobeCache.release(this.globe);
|
|
384
|
-
|
|
430
|
+
pieceOfPieProgramCache.release(this.globe);
|
|
385
431
|
CircleCache.release(this.globe);
|
|
386
432
|
AngledLineProgramCache.release(this.globe);
|
|
387
433
|
this.isFreed = true;
|
|
@@ -418,7 +464,7 @@ const integralSec = (angle) => {
|
|
|
418
464
|
return Math.log(Math.tan(angle / 2 + Math.PI / 4));
|
|
419
465
|
}
|
|
420
466
|
|
|
421
|
-
const textWriterGetOrThrow = mapGetOrThrow("textContextInjection id does not exist in map
|
|
467
|
+
const textWriterGetOrThrow = mapGetOrThrow("BearingLine textContextInjection id does not exist in map")
|
|
422
468
|
|
|
423
469
|
const calculateStartAngle = (long, lat, endLong, endLat) => {
|
|
424
470
|
const dLat = (integralSec(endLat) - integralSec(lat)); // Because lines are strectes toward poles.
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import { programCache as ringProgramCache } from '../partialrings/program';
|
|
2
|
+
import { LineOnGlobeCache } from '../programs/line-on-globe/naive';
|
|
3
|
+
import { CircleCache } from '../programs/line-on-globe/circle';
|
|
4
|
+
import { BufferOrchestrator, BufferManager } from '../util/account';
|
|
5
|
+
import { AngledLineProgramCache } from '../programs/line-on-globe/angled-line';
|
|
6
|
+
import { mapGetOrThrow } from "../util/check/get";
|
|
7
|
+
import { ContextTextWriter } from '../write-text/context-text'
|
|
8
|
+
export const RINGPARTIAL_DRAW_MODE = Object.freeze({
|
|
9
|
+
LINE_STRIP: "LINE_STRIP",
|
|
10
|
+
TRIANGLE_FAN: "TRIANGLE_FAN",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const constraintFloat = (x, lowerBound, upperBound) => {
|
|
14
|
+
if (typeof x !== "number") throw new Error("type must be numberic")
|
|
15
|
+
if (lowerBound > x || x > upperBound) throw new Error(`input must be between ${lowerBound} - ${upperBound}`)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {Object}textContextInjection
|
|
23
|
+
* @property {string} id
|
|
24
|
+
* @property {function} coordsAdaptor
|
|
25
|
+
* @property {function} textAdaptor
|
|
26
|
+
* @property {ContextTextWriter} writer
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
export default class Plugin {
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @param {*} id
|
|
34
|
+
* @param {Map<[K ,{writer:ContextTextWriter, coordsAdaptor, textAdaptor}]} textContextInjectionMap import { ContextTextWriter } from '@pirireis/webglobeplugins/write-text/context-text';
|
|
35
|
+
*
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
constructor(id, { opacity = 1, textContextInjectionMap = new Map(), drawVRM = true, drawBearingLine = true, drawAngleRing = true, drawText = true } = {}) {
|
|
39
|
+
this.id = id;
|
|
40
|
+
this._opacity = opacity;
|
|
41
|
+
this.bufferOrchestrator = new BufferOrchestrator({ capacity: 10 });
|
|
42
|
+
this._checkTextContextInjectionMap(textContextInjectionMap);
|
|
43
|
+
this._textContextInjectionMap = textContextInjectionMap;
|
|
44
|
+
this.drawVRM = drawVRM;
|
|
45
|
+
this.drawBearingLine = drawBearingLine;
|
|
46
|
+
this.drawAngleRing = drawAngleRing;
|
|
47
|
+
this.drawText = drawText;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setDoDrawVRM(bool) {
|
|
51
|
+
if (bool === this.drawVRM) return;
|
|
52
|
+
this.drawVRM = bool;
|
|
53
|
+
this.globe.DrawRender();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
setDoDrawText(bool) {
|
|
57
|
+
if (bool === this.drawText) return;
|
|
58
|
+
this.drawText = bool;
|
|
59
|
+
this.globe.DrawRender();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
setDoDrawText(bool) {
|
|
63
|
+
if (bool === this.drawText) return;
|
|
64
|
+
this.drawText = bool;
|
|
65
|
+
this.globe.DrawRender();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
_checkTextContextInjectionMap(textContextInjectionMap) {
|
|
69
|
+
if (!(textContextInjectionMap instanceof Map)) throw new TypeError("textContextInjectionMap is not instance of Map");
|
|
70
|
+
textContextInjectionMap.forEach((v) => {
|
|
71
|
+
if (typeof v !== 'object') throw new TypeError("textContextInjectionMap format is wrong");
|
|
72
|
+
if (typeof v.coordsAdaptor !== 'function') throw new TypeError("textContextInjectionMap coordsAdaptor format is wrong");
|
|
73
|
+
if (typeof v.textAdaptor !== 'function') throw new TypeError("textContextInjectionMap textAdaptor format is wrong");
|
|
74
|
+
if (!(v.writer instanceof ContextTextWriter)) throw new TypeError("textContextInjectionMap writer is not instance of ContextTextWriter");
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
setDoDrawAngleRing(bool) {
|
|
79
|
+
if (bool === this.drawAngleRing) return;
|
|
80
|
+
this.drawAngleRing = bool;
|
|
81
|
+
this.globe.DrawRender();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
settextContextInjectionMap(textContextInjectionMap, data = null) {
|
|
86
|
+
this._textContextInjectionMap = textContextInjectionMap;
|
|
87
|
+
this._textContextInjectionMap.forEach(({ writer }) => writer.clear());
|
|
88
|
+
if (data) {
|
|
89
|
+
for (const item of data) {
|
|
90
|
+
this._insertTexts(item);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
setOpacity(opacity) {
|
|
97
|
+
constraintFloat(opacity, 0, 1);
|
|
98
|
+
this._opacity = opacity;
|
|
99
|
+
this._textContextInjectionMap.forEach(({ writer }) => writer.setOpacity(opacity));
|
|
100
|
+
this.globe.DrawRender();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
init(globe, gl) {
|
|
107
|
+
this.gl = gl;
|
|
108
|
+
this.globe = globe;
|
|
109
|
+
this.lineProgram = LineOnGlobeCache.get(globe);
|
|
110
|
+
this.ringProgram = ringProgramCache.get(globe);
|
|
111
|
+
this.angledLineProgram = AngledLineProgramCache.get(globe);
|
|
112
|
+
this.circleProgram = CircleCache.get(globe);
|
|
113
|
+
// this.angleTextContext = new ContextTextWriter(globe);
|
|
114
|
+
// this.distanceTextContext = new ContextTextWriter(globe);
|
|
115
|
+
{
|
|
116
|
+
// createBuffers
|
|
117
|
+
const bufferType = "DYNAMIC_DRAW";
|
|
118
|
+
const initialCapacity = this.bufferOrchestrator.capacity;
|
|
119
|
+
this.bufferManagersCompMap = new Map(
|
|
120
|
+
[
|
|
121
|
+
["centerCoords", {
|
|
122
|
+
'bufferManager': new BufferManager(gl, 2, { bufferType, initialCapacity }),
|
|
123
|
+
'adaptor': (item) => new Float32Array([item.long, item.lat]),
|
|
124
|
+
}],
|
|
125
|
+
["targetCoords", {
|
|
126
|
+
'bufferManager': new BufferManager(gl, 2, { bufferType, initialCapacity }),
|
|
127
|
+
'adaptor': (item) => new Float32Array([item.endLong, item.endLat])
|
|
128
|
+
}],
|
|
129
|
+
["startAngle", {
|
|
130
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
131
|
+
'adaptor': (item) => new Float32Array([item.startAngle])
|
|
132
|
+
}],
|
|
133
|
+
["tailAngle", {
|
|
134
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
135
|
+
'adaptor': (item) => new Float32Array([item.tailAngle])
|
|
136
|
+
}],
|
|
137
|
+
["rgba", {
|
|
138
|
+
'bufferManager': new BufferManager(gl, 4, { bufferType, initialCapacity }),
|
|
139
|
+
'adaptor': (item) => new Float32Array(item.rgba)
|
|
140
|
+
}],
|
|
141
|
+
["radius", {
|
|
142
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
143
|
+
'adaptor': (item) => new Float32Array([item.radius])
|
|
144
|
+
}],
|
|
145
|
+
["rgbaMode", {
|
|
146
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
147
|
+
'adaptor': (item) => new Float32Array([item.rgbaMode])
|
|
148
|
+
}],
|
|
149
|
+
["dashRatio", {
|
|
150
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
151
|
+
'adaptor': (item) => new Float32Array([item.dashRatio])
|
|
152
|
+
}],
|
|
153
|
+
["bearingAngle", {
|
|
154
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
155
|
+
'adaptor': (item) => new Float32Array([item.bearingAngle])
|
|
156
|
+
}],
|
|
157
|
+
["bigRadius", {
|
|
158
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
159
|
+
'adaptor': (item) => new Float32Array([item.bigRadius])
|
|
160
|
+
}],
|
|
161
|
+
["dashOpacity", {
|
|
162
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
163
|
+
'adaptor': (item) => new Float32Array([item.dashOpacity])
|
|
164
|
+
}],
|
|
165
|
+
["circleDashAngle", {
|
|
166
|
+
'bufferManager': new BufferManager(gl, 1, { bufferType, initialCapacity }),
|
|
167
|
+
'adaptor': (item) => new Float32Array([item.circleDashAngle / 360])
|
|
168
|
+
}]
|
|
169
|
+
]
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
const obj = function (bufferManagerComp) {
|
|
175
|
+
return { 'buffer': bufferManagerComp.bufferManager.buffer, 'stride': 0, 'offset': 0 }
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
this.lineVao = this.lineProgram.createVAO(
|
|
179
|
+
...['centerCoords', 'targetCoords', 'dashRatio', 'dashOpacity', 'rgba'].map(key => obj(this.bufferManagersCompMap.get(key))));
|
|
180
|
+
this.ringVao = this.ringProgram.createVAO(
|
|
181
|
+
...['centerCoords', 'startAngle', 'tailAngle', 'rgba', 'radius', 'rgbaMode'].map(key => obj(this.bufferManagersCompMap.get(key))));
|
|
182
|
+
{
|
|
183
|
+
const angledLineBuffers = ["centerCoords", "bearingAngle", "bigRadius", "rgba", "dashRatio"].map(key => obj(this.bufferManagersCompMap.get(key)));
|
|
184
|
+
// dashOpacity is same as rgba.a to eleminate effect of dashOpacity.
|
|
185
|
+
const colorBuffer = this.bufferManagersCompMap.get("rgba");
|
|
186
|
+
angledLineBuffers.push(
|
|
187
|
+
{ 'buffer': colorBuffer.bufferManager.buffer, 'stride': 16, 'offset': 12 },
|
|
188
|
+
)
|
|
189
|
+
this.angledLineVao = this.angledLineProgram.createVAO(...angledLineBuffers);
|
|
190
|
+
}
|
|
191
|
+
// centerObj, startAngleObj, radiusObj, colorObj, dashRatioObj, dashOpacityObj
|
|
192
|
+
this.circleVao = this.circleProgram.createVAO(
|
|
193
|
+
...["centerCoords", "bigRadius", "rgba", "circleDashAngle", "dashOpacity"].map(key => obj(this.bufferManagersCompMap.get(key))));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
draw3D() {
|
|
199
|
+
const { gl } = this;
|
|
200
|
+
this.lineProgram.draw(this.lineVao, this.bufferOrchestrator.length, this._opacity);
|
|
201
|
+
gl.disable(gl.DEPTH_TEST);
|
|
202
|
+
if (this.drawAngleRing) {
|
|
203
|
+
this.ringProgram.draw(this.bufferOrchestrator.length, this.ringVao, 360, this._opacity * 0.8, RINGPARTIAL_DRAW_MODE.TRIANGLE_FAN);
|
|
204
|
+
this.ringProgram.draw(this.bufferOrchestrator.length, this.ringVao, 360, this._opacity, RINGPARTIAL_DRAW_MODE.LINE_STRIP);
|
|
205
|
+
}
|
|
206
|
+
if (this.drawBearingLine) {
|
|
207
|
+
this.angledLineProgram.draw(this.angledLineVao, this.bufferOrchestrator.length, this._opacity * 0.8);
|
|
208
|
+
}
|
|
209
|
+
if (this.drawVRM) {
|
|
210
|
+
this.circleProgram.draw(this.circleVao, this.bufferOrchestrator.length, this._opacity);
|
|
211
|
+
}
|
|
212
|
+
if (this.drawText) {
|
|
213
|
+
this._textContextInjectionMap.forEach((e) => { e.writer.draw(); });
|
|
214
|
+
}
|
|
215
|
+
gl.enable(gl.DEPTH_TEST);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @typedef {{key, long, lat, endLong, endLat, bearingAngle, radius, rgba:[4numbers], rgbaMode, bigRadius, dashRatio, dashOpacity, circleDashAngle}} item
|
|
221
|
+
* @property {string} key
|
|
222
|
+
* @property {number} long
|
|
223
|
+
* @property {number} lat
|
|
224
|
+
* @property {number} endLong
|
|
225
|
+
* @property {number} endLat
|
|
226
|
+
* @property {number} bearingAngle 0-360
|
|
227
|
+
* @property {number} radius angle ring radius
|
|
228
|
+
* @property {Array<4numbers>} rgba [r,g,b,a]
|
|
229
|
+
* @property {number} rgbaMode 0 constant, 1 fading, 2 hides angle ring
|
|
230
|
+
* @property {number} bigRadius undefined means it will be calculated from long, lat, endLong, endLat
|
|
231
|
+
* @property {number} dashRatio 0-1
|
|
232
|
+
* @property {number} dashOpacity 0-1
|
|
233
|
+
* @property {number} circleDashAngle 0-360
|
|
234
|
+
* @param {Array<item>} items
|
|
235
|
+
* @param {Array<string>} textWriterInjectionSubSetIDs | textContextInjectionMap keys to be used for writing text.
|
|
236
|
+
*/
|
|
237
|
+
insertBulk(items, textWriterInjectionSubSetIDs = []) {
|
|
238
|
+
const { globe, bufferOrchestrator, bufferManagersCompMap } = this;// angleTextContext, distanceTextContext,
|
|
239
|
+
const textWriterInjectionSubSets = textWriterGetOrThrow(this._textContextInjectionMap, textWriterInjectionSubSetIDs);
|
|
240
|
+
const data = []
|
|
241
|
+
for (let item of items) {
|
|
242
|
+
this._insertTexts(item, textWriterInjectionSubSets);
|
|
243
|
+
data.push(this.__insertAdaptor(item));
|
|
244
|
+
}
|
|
245
|
+
bufferOrchestrator.insertBulk(data, bufferManagersCompMap);
|
|
246
|
+
this._textContextInjectionMap.forEach((v) => {
|
|
247
|
+
const { writer } = v;
|
|
248
|
+
writer.updateOpacityBulk(items, (e) => e.key, (e) => e.rgba[3]);
|
|
249
|
+
})
|
|
250
|
+
globe.DrawRender();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
deleteBulk(keys) {
|
|
257
|
+
this.bufferOrchestrator.deleteBulk(keys, this.bufferManagersCompMap, ["radius", "centerCoords", "targetCoords", "rgba"]);
|
|
258
|
+
this._deleteTexts(keys);
|
|
259
|
+
this.globe.DrawRender();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
defrag() {
|
|
264
|
+
this.bufferOrchestrator.defrag(this.bufferManagersCompMap);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
*
|
|
269
|
+
* @param {Array<{key, long, lat, endLong, endLat, bearingAngle}>} items
|
|
270
|
+
* @param {Array<string>} textWriterInjectionSubSetIDs | textContextInjectionMap keys to be used for writing text.
|
|
271
|
+
*/
|
|
272
|
+
updateCoordinatesBulk(items, textWriterInjectionSubSetIDs = []) { //TODO
|
|
273
|
+
const injectionsSubSet = textWriterGetOrThrow(this._textContextInjectionMap, textWriterInjectionSubSetIDs);;
|
|
274
|
+
const { globe, bufferOrchestrator, bufferManagersCompMap, } = this;
|
|
275
|
+
const data = []
|
|
276
|
+
for (let item of items) {
|
|
277
|
+
this._insertTexts(item, injectionsSubSet);
|
|
278
|
+
data.push(this.__updateCoordsAdaptor(item));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
bufferOrchestrator.updateBulk(data, bufferManagersCompMap, ["centerCoords", "targetCoords", "startAngle", "tailAngle", "bearingAngle", "bigRadius", "radius"]);
|
|
282
|
+
globe.DrawRender();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
*
|
|
288
|
+
* @param {*} items some colums EXCEPT positional ones
|
|
289
|
+
* @param {string} propertyIDs
|
|
290
|
+
* @param {string} textWriterInjectionSubSetIDs
|
|
291
|
+
* Do NOT send empty data if property ID of this data is entered or NaN is loaded to the buffer, resulting in an unwanted behaviour.
|
|
292
|
+
*/
|
|
293
|
+
updatePartial(items, propertyIDs = [], textWriterInjectionSubSetIDs = []) { // textWriterInjectionSubSetIDs = []
|
|
294
|
+
if (propertyIDs.length === 0) console.warn("updatePartial is called with no target propertyIDs");
|
|
295
|
+
const { _textContextInjectionMap, bufferOrchestrator, bufferManagersCompMap } = this;
|
|
296
|
+
const writers = textWriterGetOrThrow(this._textContextInjectionMap, textWriterInjectionSubSetIDs);
|
|
297
|
+
for (let item of items) { this._insertTexts(item, writers) }
|
|
298
|
+
bufferOrchestrator.updateBulk(items, bufferManagersCompMap, propertyIDs);
|
|
299
|
+
// patch to update text opacity
|
|
300
|
+
for (const property of propertyIDs) {
|
|
301
|
+
if (property === "rgba") {
|
|
302
|
+
_textContextInjectionMap.forEach((v) => {
|
|
303
|
+
const { writer } = v;
|
|
304
|
+
writer.updateOpacityBulk(items, (e) => e.key, (e) => e.rgba[3]);
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
this.globe.DrawRender();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
__insertAdaptor(item) {
|
|
313
|
+
const lat = radian(item.lat)
|
|
314
|
+
const long = radian(item.long)
|
|
315
|
+
const endLat = radian(item.endLat)
|
|
316
|
+
const endLong = radian(item.endLong)
|
|
317
|
+
const rgba = item.rgba !== undefined ? item.rgba : [0, 0, 0, 0];
|
|
318
|
+
const rgbaMode = item.rgbaMode !== undefined ? item.rgbaMode : 0;
|
|
319
|
+
const dashRatio = item.dashRatio !== undefined ? item.dashRatio : 1.0;
|
|
320
|
+
const dashOpacity = item.dashOpacity !== undefined ? item.dashOpacity : 0.9;
|
|
321
|
+
const circleDashAngle = item.circleDashAngle !== undefined ? item.circleDashAngle : 360;
|
|
322
|
+
const bigRadius = item.bigRadius !== undefined ? item.bigRadius : this.globe.Math.GetDist3D(item.long, item.lat, item.endLong, item.endLat);
|
|
323
|
+
const radius = item.radius !== undefined ? item.radius : bigRadius * 0.2;
|
|
324
|
+
const startAngle = calculateStartAngle(long, lat, endLong, endLat);
|
|
325
|
+
const bearingAngle = radian(item.bearingAngle - 90);
|
|
326
|
+
let tailAngle = bearingAngle - startAngle;
|
|
327
|
+
if (tailAngle > 0) {
|
|
328
|
+
tailAngle -= Math.PI * 2;
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
key: item.key,
|
|
332
|
+
lat,
|
|
333
|
+
long,
|
|
334
|
+
endLat,
|
|
335
|
+
endLong,
|
|
336
|
+
bearingAngle,
|
|
337
|
+
radius,
|
|
338
|
+
bigRadius,
|
|
339
|
+
startAngle,
|
|
340
|
+
tailAngle,
|
|
341
|
+
rgba,
|
|
342
|
+
dashRatio,
|
|
343
|
+
dashOpacity,
|
|
344
|
+
circleDashAngle,
|
|
345
|
+
rgbaMode
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
__updateCoordsAdaptor(item) {
|
|
350
|
+
const lat = radian(item.lat)
|
|
351
|
+
const long = radian(item.long)
|
|
352
|
+
const endLat = radian(item.endLat)
|
|
353
|
+
const endLong = radian(item.endLong)
|
|
354
|
+
const bigRadius = item.bigRadius !== undefined ? item.bigRadius : this.globe.Math.GetDist3D(item.long, item.lat, item.endLong, item.endLat);
|
|
355
|
+
const radius = item.radius !== undefined ? item.radius : bigRadius * 0.2;
|
|
356
|
+
const startAngle = calculateStartAngle(long, lat, endLong, endLat);
|
|
357
|
+
const bearingAngle = radian(item.bearingAngle - 90);
|
|
358
|
+
let tailAngle = bearingAngle - startAngle;
|
|
359
|
+
if (tailAngle > 0) {
|
|
360
|
+
tailAngle -= Math.PI * 2;
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
key: item.key,
|
|
364
|
+
lat,
|
|
365
|
+
long,
|
|
366
|
+
endLat,
|
|
367
|
+
endLong,
|
|
368
|
+
bearingAngle,
|
|
369
|
+
radius,
|
|
370
|
+
bigRadius,
|
|
371
|
+
startAngle,
|
|
372
|
+
tailAngle,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
//TODO free
|
|
378
|
+
free() {
|
|
379
|
+
if (this.isFreed) return;
|
|
380
|
+
this.bufferManagersCompMap.forEach(({ bufferManager, adaptor }) => {
|
|
381
|
+
bufferManager.free();
|
|
382
|
+
});
|
|
383
|
+
LineOnGlobeCache.release(this.globe);
|
|
384
|
+
ringProgramCache.release(this.globe);
|
|
385
|
+
CircleCache.release(this.globe);
|
|
386
|
+
AngledLineProgramCache.release(this.globe);
|
|
387
|
+
this.isFreed = true;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
_insertTexts(item, textWriterInjectionSubSet) {
|
|
394
|
+
//TODO This method can be more performant if it works horizontally, tabular
|
|
395
|
+
textWriterInjectionSubSet.forEach((v) => {
|
|
396
|
+
const { coordsAdaptor, textAdaptor, writer } = v
|
|
397
|
+
const { lat, long } = coordsAdaptor(item);
|
|
398
|
+
const text = textAdaptor(item);
|
|
399
|
+
writer.insertText(item.key, lat, long, text);
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
_deleteTexts(keys) {
|
|
405
|
+
this._textContextInjectionMap.forEach((e) => {
|
|
406
|
+
e.writer.deleteTextBulk(keys);
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
const radian = (degree) => degree * Math.PI / 180;
|
|
416
|
+
|
|
417
|
+
const integralSec = (angle) => {
|
|
418
|
+
return Math.log(Math.tan(angle / 2 + Math.PI / 4));
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const textWriterGetOrThrow = mapGetOrThrow("BearingLine textContextInjection id does not exist in map")
|
|
422
|
+
|
|
423
|
+
const calculateStartAngle = (long, lat, endLong, endLat) => {
|
|
424
|
+
const dLat = (integralSec(endLat) - integralSec(lat)); // Because lines are strectes toward poles.
|
|
425
|
+
const dLong = endLong - long;
|
|
426
|
+
|
|
427
|
+
let angle = -Math.atan2(dLat, dLong);
|
|
428
|
+
return angle;
|
|
429
|
+
}
|
|
430
|
+
|
package/package.json
CHANGED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This class registered as globe plugin one for each globe. to the head of plugin call stack.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
class PinPointTotem {
|
|
6
|
+
constructor(globe) {
|
|
7
|
+
|
|
8
|
+
this.objectArrayMap = new Map()
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* objectArray
|
|
14
|
+
* callbacks ={
|
|
15
|
+
* mouseDown,
|
|
16
|
+
* mouseMove,
|
|
17
|
+
* mouseUp,
|
|
18
|
+
* mouseClick,
|
|
19
|
+
* mouseDbClick,
|
|
20
|
+
* keyDown,
|
|
21
|
+
* keyUp
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
registerPinMap(objectArray, callbacks) {
|
|
25
|
+
this.objectArrayMap.set(objectArray, callbacks);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
unregisterPinMap(objectArray) {
|
|
29
|
+
if (this.objectArrayMap.has(objectArray)) {
|
|
30
|
+
this.objectArrayMap.delete(objectArray);
|
|
31
|
+
} else {
|
|
32
|
+
console.warn('PinPointTotem objectArrayMap does not contain the objectArray')
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
// GlobeMethods
|
|
38
|
+
// haritada sol butona basıldığında çağrılır
|
|
39
|
+
mouseDown(x, y, event) {
|
|
40
|
+
|
|
41
|
+
return false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// mouse'a basılıp hareket ettirildiğinde, mouseDown'dan true dönmüşse çağrılır
|
|
45
|
+
mouseMove(x, y, event) {
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// mouse up'ın left'i mouseDown'dan true dönmüşse çağrılır, edit mode içindir
|
|
50
|
+
// right'i ise sağ tıka basılıp bırakıldığında çağrılır
|
|
51
|
+
mouseUp(x, y, event) {
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// harita üzerinde tıklandığında çağrılır
|
|
56
|
+
mouseClick(x, y, event) {
|
|
57
|
+
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// harita üzerinde çift tıklandığında çağrılır
|
|
62
|
+
mouseDblClick(x, y, event) {
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// klavyeden bir tuşa basıldığı anda ve tuşa basılı kalınmaya devam edildiği durumlarda çalışır
|
|
67
|
+
keyDown(event) {
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// klavyedeki bir tuştan parmak çekildiği anda çalışır
|
|
72
|
+
keyUp(event) {
|
|
73
|
+
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
}
|
|
@@ -58,7 +58,7 @@ void main() {
|
|
|
58
58
|
v_limp = vec2(0.0, 0.0);
|
|
59
59
|
} else {
|
|
60
60
|
vec2 position;
|
|
61
|
-
if ( radius <
|
|
61
|
+
if ( radius < 0.0) {
|
|
62
62
|
float cosine1 = cos(asin(tanh(center_position.y / 6378137.0)));
|
|
63
63
|
position = circleLimpFromLongLatRadCenterMercatorCompass_accurate(center_position, radius / cosine1 , angle);
|
|
64
64
|
} else {
|
|
@@ -5,7 +5,7 @@ import { noRegisterGlobeProgramCache, globeProgramCache } from "../programcache"
|
|
|
5
5
|
import BufferOffsetManger from "../../util/account/bufferoffsetmanager";
|
|
6
6
|
import { programCache } from "../rings/distancering/circleflatprogram";
|
|
7
7
|
|
|
8
|
-
const GLOBE_MIDPOINT_COUNT =
|
|
8
|
+
const GLOBE_MIDPOINT_COUNT = 30;
|
|
9
9
|
const ITEM_SIZE = 9;
|
|
10
10
|
|
|
11
11
|
const vertexShader = `#version 300 es
|
|
@@ -17,7 +17,7 @@ ${mercatorXYToGLPosition}
|
|
|
17
17
|
${cartesian3DToGLPosition}
|
|
18
18
|
|
|
19
19
|
in vec2 start_position;
|
|
20
|
-
in vec3
|
|
20
|
+
in vec3 start_position3d;
|
|
21
21
|
in vec2 end_position;
|
|
22
22
|
in vec3 end_position3d;
|
|
23
23
|
in float dash_ratio;
|
|
@@ -34,7 +34,12 @@ void main() {
|
|
|
34
34
|
vec2 longLat;
|
|
35
35
|
if (is3D) {
|
|
36
36
|
interpolation = float(gl_VertexID) / ${GLOBE_MIDPOINT_COUNT - 1}.0;
|
|
37
|
-
vec3 cartesian
|
|
37
|
+
vec3 cartesian;
|
|
38
|
+
if ( length( start_position3d - end_position3d) < 4.4){
|
|
39
|
+
cartesian = mix( start_position3d, end_position3d, interpolation);
|
|
40
|
+
} else {
|
|
41
|
+
cartesian = pointsOnSphereBetween(start_position3d, end_position3d, interpolation) * length(end_position3d);
|
|
42
|
+
}
|
|
38
43
|
gl_Position = cartesian3DToGLPosition(cartesian);
|
|
39
44
|
v_limp = vec2(0.0, 0.0);
|
|
40
45
|
} else {
|
|
@@ -124,11 +129,9 @@ class Logic {
|
|
|
124
129
|
this._lastOpacity = opacity;
|
|
125
130
|
}
|
|
126
131
|
const drawCount = globe.api_GetCurrentGeometry() === 0 ? GLOBE_MIDPOINT_COUNT : 2;
|
|
127
|
-
// gl.disable(gl.DEPTH_TEST);
|
|
128
132
|
gl.drawArraysInstanced(gl.LINE_STRIP, 0, drawCount, length);
|
|
129
133
|
gl.bindVertexArray(null);
|
|
130
134
|
cameraBlockTotem.unbind(cameraBlockBindingPoint);
|
|
131
|
-
// gl.enable(gl.DEPTH_TEST);
|
|
132
135
|
}
|
|
133
136
|
|
|
134
137
|
//
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { createProgram, shaderfunctions } from "../../../util";
|
|
2
|
+
import { CameraUniformBlockTotem, CameraUniformBlockString } from "../../totems";
|
|
3
|
+
import { noRegisterGlobeProgramCache, globeProgramCache } from "../../programcache";
|
|
4
|
+
import {
|
|
5
|
+
POLE,
|
|
6
|
+
PI,
|
|
7
|
+
longLatRadToMercator,
|
|
8
|
+
mercatorXYToGLPosition,
|
|
9
|
+
longLatRadToCartesian3D,
|
|
10
|
+
circleLimpFromLongLatRadCenterCartesian3D_accurate,
|
|
11
|
+
circleLimpFromLongLatRadCenterMercatorCompass_accurate,
|
|
12
|
+
circleLimpFromLongLatRadCenterMercatorRealDistanceNew_accurate,
|
|
13
|
+
cartesian3DToGLPosition
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
} from "../../../util/shaderfunctions/geometrytransformations";
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* TODO:
|
|
21
|
+
* 1. Triangle face looks at screen. if rotation angle is positive the last vertex must be the faintest.
|
|
22
|
+
*
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const drawModeMap = Object.freeze({
|
|
26
|
+
LINE_STRIP: 0,
|
|
27
|
+
TRIANGLE_FAN: 1,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
const vertexShaderSource = `#version 300 es
|
|
33
|
+
|
|
34
|
+
${CameraUniformBlockString}
|
|
35
|
+
${PI}
|
|
36
|
+
${longLatRadToMercator}
|
|
37
|
+
${mercatorXYToGLPosition}
|
|
38
|
+
${longLatRadToCartesian3D}
|
|
39
|
+
${circleLimpFromLongLatRadCenterCartesian3D_accurate}
|
|
40
|
+
${circleLimpFromLongLatRadCenterMercatorCompass_accurate}
|
|
41
|
+
${circleLimpFromLongLatRadCenterMercatorRealDistanceNew_accurate}
|
|
42
|
+
${cartesian3DToGLPosition}
|
|
43
|
+
|
|
44
|
+
uniform float edge_count;
|
|
45
|
+
uniform int draw_mode; // %2 => 0: LINE_STRIP, 1: TRIANGLE_FAN
|
|
46
|
+
uniform float plugin_alpha_multiplier;
|
|
47
|
+
//, lat, startAngle, tailAngle, ...rgba, radius, rgbaMode
|
|
48
|
+
// in vec2 center; // long, lat in radian
|
|
49
|
+
in vec2 center2d;
|
|
50
|
+
in vec3 center3d;
|
|
51
|
+
in float start_angle2d;
|
|
52
|
+
in float tail_angle2d;
|
|
53
|
+
|
|
54
|
+
in float start_angle3d;
|
|
55
|
+
in float tail_angle3d;
|
|
56
|
+
|
|
57
|
+
in vec4 color;
|
|
58
|
+
in float radius; // in meter
|
|
59
|
+
in float color_mode; // 0.0: constant, 1.0: fading, 2.0: hide
|
|
60
|
+
// flat out int vid;
|
|
61
|
+
// flat out float v_phase;
|
|
62
|
+
out vec2 v_pos;
|
|
63
|
+
out vec4 v_color;
|
|
64
|
+
// flat out float v_angle;
|
|
65
|
+
|
|
66
|
+
void main() {
|
|
67
|
+
// vid = gl_VertexID;
|
|
68
|
+
if (color_mode == 2.0 || radius == 0.0) { return; }
|
|
69
|
+
float start_angle, tail_angle;
|
|
70
|
+
if (is3D) {
|
|
71
|
+
start_angle = start_angle3d;
|
|
72
|
+
tail_angle = tail_angle3d;
|
|
73
|
+
} else {
|
|
74
|
+
start_angle = start_angle2d;
|
|
75
|
+
tail_angle = tail_angle2d;
|
|
76
|
+
}
|
|
77
|
+
float color_mode_ = color_mode;
|
|
78
|
+
if ( draw_mode == 0 && color_mode == 1.0) {color_mode_ = 0.0;}
|
|
79
|
+
float vertexID = float(gl_VertexID);
|
|
80
|
+
float radius_ = radius;
|
|
81
|
+
float alpha = plugin_alpha_multiplier;
|
|
82
|
+
if (draw_mode == 1) { // TRIANGLE_FAN
|
|
83
|
+
if (gl_VertexID == 0) {
|
|
84
|
+
radius_ = 0.0;
|
|
85
|
+
if ( color_mode == 1.0 ) { alpha = 0.0; }
|
|
86
|
+
}
|
|
87
|
+
vertexID -= 1.0;
|
|
88
|
+
}
|
|
89
|
+
float phase = ( vertexID / (edge_count - 1.0) );
|
|
90
|
+
// v_angle = tail_angle;
|
|
91
|
+
|
|
92
|
+
if ( color_mode_ == 1.0 ) {
|
|
93
|
+
if ( tail_angle < 0.0 ) {
|
|
94
|
+
v_color = vec4( color.rgb , color.a * ( 1.0 - phase ) * alpha );
|
|
95
|
+
} else {
|
|
96
|
+
v_color = vec4( color.rgb , color.a * phase * alpha );
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
v_color = vec4( color.rgb , color.a * alpha );
|
|
100
|
+
}
|
|
101
|
+
if ( color_mode == 0.0 && draw_mode == 1 ) {
|
|
102
|
+
v_color.a /= 2.0;
|
|
103
|
+
}
|
|
104
|
+
float angle;
|
|
105
|
+
if ( tail_angle > 0.0 ) {
|
|
106
|
+
angle = tail_angle * (-phase + 1.0) + start_angle;
|
|
107
|
+
} else {
|
|
108
|
+
angle = tail_angle * phase + start_angle;
|
|
109
|
+
}
|
|
110
|
+
if (is3D) {
|
|
111
|
+
vec3 pos = circleLimpFromLongLatRadCenterCartesian3D_accurate(center3d, radius_, angle);
|
|
112
|
+
v_pos = vec2(0.0, 0.0);
|
|
113
|
+
gl_Position = cartesian3DToGLPosition(pos);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
vec2 pos2 = circleLimpFromLongLatRadCenterMercatorRealDistanceNew_accurate(center2d, radius_, angle);
|
|
117
|
+
v_pos = pos2;
|
|
118
|
+
gl_Position = mercatorXYToGLPosition(pos2);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
gl_PointSize = 10.0;
|
|
122
|
+
}`;
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
const fragmentShaderSource = `#version 300 es` + POLE + PI + `
|
|
126
|
+
precision highp float;
|
|
127
|
+
// flat in int vid;
|
|
128
|
+
in vec4 v_color;
|
|
129
|
+
in vec2 v_pos;
|
|
130
|
+
// flat in float v_phase;
|
|
131
|
+
// in float v_angle;
|
|
132
|
+
out vec4 outColor;
|
|
133
|
+
void main() {
|
|
134
|
+
// if( vid % 2 == 0 ) { discard; }
|
|
135
|
+
// if ( mod(v_angle, PI / 36.0 ) < (PI / 72.0)) { discard; }
|
|
136
|
+
// if ( mod(v_angle * v_phase, PI / 90.0 ) < (PI / 180.0)) { discard; }
|
|
137
|
+
if ( v_pos.x < -POLE || v_pos.x > POLE || v_pos.y < -POLE || v_pos.y > POLE ) { discard; }
|
|
138
|
+
outColor = v_color;
|
|
139
|
+
}`;
|
|
140
|
+
|
|
141
|
+
export const ITEM_SIZE = 10;
|
|
142
|
+
|
|
143
|
+
export class Logic {
|
|
144
|
+
|
|
145
|
+
constructor(globe) {
|
|
146
|
+
this.globe = globe;
|
|
147
|
+
this.gl = globe.gl;
|
|
148
|
+
this._lastMode = 0;
|
|
149
|
+
this._lastEdgeCount = 64;
|
|
150
|
+
this._lastAlphaMultiplier = 1.0;
|
|
151
|
+
|
|
152
|
+
this.program = createProgram(this.gl, vertexShaderSource, fragmentShaderSource);
|
|
153
|
+
const { gl, program } = this;
|
|
154
|
+
{ // set attributes locations
|
|
155
|
+
gl.bindAttribLocation(program, 0, 'center2d');
|
|
156
|
+
gl.bindAttribLocation(program, 1, 'center3d')
|
|
157
|
+
gl.bindAttribLocation(program, 2, 'start_angle2d');
|
|
158
|
+
gl.bindAttribLocation(program, 3, 'tail_angle2d');
|
|
159
|
+
gl.bindAttribLocation(program, 4, 'start_angle3d');
|
|
160
|
+
gl.bindAttribLocation(program, 5, 'tail_angle3d');
|
|
161
|
+
gl.bindAttribLocation(program, 6, 'color');
|
|
162
|
+
gl.bindAttribLocation(program, 7, 'radius');
|
|
163
|
+
gl.bindAttribLocation(program, 8, 'color_mode');
|
|
164
|
+
// vao
|
|
165
|
+
// instanced draw read 1
|
|
166
|
+
}
|
|
167
|
+
{ // Uniforms
|
|
168
|
+
this._edgeCountLocation = gl.getUniformLocation(program, 'edge_count');
|
|
169
|
+
this._draw_modeLocation = gl.getUniformLocation(program, 'draw_mode');
|
|
170
|
+
this._plugin_alpha_multiplierLocation = gl.getUniformLocation(program, 'plugin_alpha_multiplier');
|
|
171
|
+
const currentProgram = gl.getParameter(gl.CURRENT_PROGRAM);
|
|
172
|
+
gl.useProgram(program);
|
|
173
|
+
gl.uniform1i(this._draw_modeLocation, this._lastMode);
|
|
174
|
+
gl.uniform1f(this._edgeCountLocation, this._lastEdgeCount);
|
|
175
|
+
gl.uniform1f(this._plugin_alpha_multiplierLocation, 1.0);
|
|
176
|
+
|
|
177
|
+
this.cameraBlockBindingPoint = 0;
|
|
178
|
+
this.cameraBlockTotem = globeProgramCache.getProgram(globe, CameraUniformBlockTotem);
|
|
179
|
+
const cameraBlockIndex = gl.getUniformBlockIndex(program, "CameraUniformBlock");
|
|
180
|
+
gl.uniformBlockBinding(program, cameraBlockIndex, this.cameraBlockBindingPoint);
|
|
181
|
+
|
|
182
|
+
gl.useProgram(currentProgram);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
draw(length, vao, edgeCount, alphaMultiplier, drawMode) {
|
|
190
|
+
const { gl, program, cameraBlockTotem, cameraBlockBindingPoint } = this
|
|
191
|
+
|
|
192
|
+
// gl.disable(gl.DEPTH_TEST);
|
|
193
|
+
gl.useProgram(program);
|
|
194
|
+
if (drawMode !== this._lastMode) {
|
|
195
|
+
gl.uniform1i(this._draw_modeLocation, drawModeMap[drawMode]);
|
|
196
|
+
this._lastMode = drawMode;
|
|
197
|
+
}
|
|
198
|
+
if (edgeCount !== this._lastEdgeCount) {
|
|
199
|
+
gl.uniform1f(this._edgeCountLocation, edgeCount);
|
|
200
|
+
this._lastEdgeCount = edgeCount;
|
|
201
|
+
}
|
|
202
|
+
if (alphaMultiplier !== this._lastAlphaMultiplier) {
|
|
203
|
+
gl.uniform1f(this._plugin_alpha_multiplierLocation, alphaMultiplier);
|
|
204
|
+
this._lastAlphaMultiplier = alphaMultiplier;
|
|
205
|
+
}
|
|
206
|
+
const overdraw = drawModeMap[drawMode];
|
|
207
|
+
cameraBlockTotem.bind(cameraBlockBindingPoint);
|
|
208
|
+
gl.bindVertexArray(vao);
|
|
209
|
+
gl.drawArraysInstanced(gl[drawMode], 0, edgeCount + overdraw, length);
|
|
210
|
+
cameraBlockTotem.unbind(cameraBlockBindingPoint);
|
|
211
|
+
gl.bindVertexArray(null);
|
|
212
|
+
// gl.enable(gl.DEPTH_TEST);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
free() {
|
|
217
|
+
noRegisterGlobeProgramCache.releaseProgram(this.globe, CameraUniformBlockTotem);
|
|
218
|
+
this.gl.deleteProgram(this.program);
|
|
219
|
+
this.program = null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* in vec2 center; // long, lat in radian
|
|
225
|
+
in float start_angle; // the start of partial circle from bearing point
|
|
226
|
+
in float tail_angle; // the rotation of the partial circle
|
|
227
|
+
in vec4 color;
|
|
228
|
+
in float radius; // in meter
|
|
229
|
+
in float color_mode; // 0.0: constant, 1.0: fading, 2.0: hide
|
|
230
|
+
*/
|
|
231
|
+
|
|
232
|
+
createVAO(center2dObj, center3dObj, startAngle2DObj, tailAngle2DObj, startAngle3DObj, tailAngle3DObj, colorObj, radiusObj, colorModeObj) {
|
|
233
|
+
|
|
234
|
+
const { gl } = this;
|
|
235
|
+
const vao = gl.createVertexArray();
|
|
236
|
+
gl.bindVertexArray(vao);
|
|
237
|
+
|
|
238
|
+
{
|
|
239
|
+
const { buffer, stride, offset } = center2dObj;
|
|
240
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
241
|
+
gl.enableVertexAttribArray(0);
|
|
242
|
+
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, stride, offset);
|
|
243
|
+
gl.vertexAttribDivisor(0, 1);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
{
|
|
247
|
+
const { buffer, stride, offset } = center3dObj;
|
|
248
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
249
|
+
gl.enableVertexAttribArray(1);
|
|
250
|
+
gl.vertexAttribPointer(1, 3, gl.FLOAT, false, stride, offset);
|
|
251
|
+
gl.vertexAttribDivisor(1, 1);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
{
|
|
255
|
+
const { buffer, stride, offset } = startAngle2DObj;
|
|
256
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
257
|
+
gl.enableVertexAttribArray(2);
|
|
258
|
+
gl.vertexAttribPointer(2, 1, gl.FLOAT, false, stride, offset);
|
|
259
|
+
gl.vertexAttribDivisor(2, 1);
|
|
260
|
+
}
|
|
261
|
+
{
|
|
262
|
+
const { buffer, stride, offset } = tailAngle2DObj;
|
|
263
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
264
|
+
gl.enableVertexAttribArray(3);
|
|
265
|
+
gl.vertexAttribPointer(3, 1, gl.FLOAT, false, stride, offset);
|
|
266
|
+
gl.vertexAttribDivisor(3, 1);
|
|
267
|
+
}
|
|
268
|
+
{
|
|
269
|
+
const { buffer, stride, offset } = startAngle3DObj;
|
|
270
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
271
|
+
gl.enableVertexAttribArray(4);
|
|
272
|
+
gl.vertexAttribPointer(4, 1, gl.FLOAT, false, stride, offset);
|
|
273
|
+
gl.vertexAttribDivisor(4, 1);
|
|
274
|
+
}
|
|
275
|
+
{
|
|
276
|
+
const { buffer, stride, offset } = tailAngle3DObj;
|
|
277
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
278
|
+
gl.enableVertexAttribArray(5);
|
|
279
|
+
gl.vertexAttribPointer(5, 1, gl.FLOAT, false, stride, offset);
|
|
280
|
+
gl.vertexAttribDivisor(5, 1);
|
|
281
|
+
}
|
|
282
|
+
{
|
|
283
|
+
const { buffer, stride, offset } = colorObj;
|
|
284
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
285
|
+
gl.enableVertexAttribArray(6);
|
|
286
|
+
gl.vertexAttribPointer(6, 4, gl.FLOAT, false, stride, offset);
|
|
287
|
+
gl.vertexAttribDivisor(6, 1);
|
|
288
|
+
}
|
|
289
|
+
{
|
|
290
|
+
const { buffer, stride, offset } = radiusObj;
|
|
291
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
292
|
+
gl.enableVertexAttribArray(7);
|
|
293
|
+
gl.vertexAttribPointer(7, 1, gl.FLOAT, false, stride, offset);
|
|
294
|
+
gl.vertexAttribDivisor(7, 1);
|
|
295
|
+
}
|
|
296
|
+
{
|
|
297
|
+
const { buffer, stride, offset } = colorModeObj;
|
|
298
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
299
|
+
gl.enableVertexAttribArray(8);
|
|
300
|
+
gl.vertexAttribPointer(8, 1, gl.FLOAT, false, stride, offset);
|
|
301
|
+
gl.vertexAttribDivisor(8, 1);
|
|
302
|
+
}
|
|
303
|
+
gl.bindVertexArray(null);
|
|
304
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
305
|
+
return vao;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export const pieceOfPieProgramCache = Object.freeze({
|
|
313
|
+
get: (globe) => noRegisterGlobeProgramCache.getProgram(globe, Logic),
|
|
314
|
+
release: (globe) => noRegisterGlobeProgramCache.releaseProgram(globe, Logic)
|
|
315
|
+
});
|
package/util/check/get.js
CHANGED
|
@@ -4,7 +4,7 @@ export const mapGetOrThrow = (errorNote) => {
|
|
|
4
4
|
const result = [];
|
|
5
5
|
for (let i = 0; i < ids.length; i++) {
|
|
6
6
|
const e = mapInstance.get(ids[i]);
|
|
7
|
-
if (e === undefined) throw new Error(errorNote + "
|
|
7
|
+
if (e === undefined) throw new Error(errorNote + " " + ids[i]);
|
|
8
8
|
result.push(e);
|
|
9
9
|
}
|
|
10
10
|
return result;
|
|
@@ -134,10 +134,11 @@ vec2 circleLimpFromLongLatRadCenterMercatorRealDistance(vec2 center, float radiu
|
|
|
134
134
|
|
|
135
135
|
float delta_long = atan(sin(ang) * sin_r * cos(center.y), cos_r - sin(center.y) * sin_lat);
|
|
136
136
|
float longi = center.x + delta_long;
|
|
137
|
+
|
|
137
138
|
|
|
138
139
|
return vec2(
|
|
139
140
|
R * longi,
|
|
140
|
-
R * log(tan(PI / 4.0 + lat / 2.0))
|
|
141
|
+
R * log(tan(PI / 4.0 + lat / 2.0))
|
|
141
142
|
);
|
|
142
143
|
}`;
|
|
143
144
|
|
|
@@ -343,10 +344,10 @@ vec2 circleLimpFromLongLatRadCenterMercatorRealDistanceNew_accurate(vec2 mercato
|
|
|
343
344
|
|
|
344
345
|
float delta_long = atan(sin(ang) * sin_r * cos(center.y), cos_r - sin(center.y) * sin_lat);
|
|
345
346
|
float longi = center.x + delta_long;
|
|
346
|
-
|
|
347
|
+
float y = mix(-80.5, 80.5, abs(lat + PI / 2.0));
|
|
347
348
|
return vec2(
|
|
348
349
|
R * longi,
|
|
349
|
-
R * log(tan(PI / 4.0 + lat / 2.0))
|
|
350
|
+
R * log(tan(PI / 4.0 + lat / 2.0)) - y
|
|
350
351
|
);
|
|
351
352
|
}`;
|
|
352
353
|
|