@pirireis/webglobeplugins 1.1.8 → 1.1.9

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.
@@ -267,10 +267,12 @@ function filterEdgePointTriangles(indeces, edgePointIndices) {
267
267
  // };
268
268
  // }
269
269
  export function partialTessellation(triangleMeta, limits, innerCuts, calculateRealEdgeArc = false) {
270
- // TODO: pointMap can be local variable and cleaned after function call to avoid reinitialization overhead
271
- const pointMap = new Map(); // Key: "lon|lat", Value: index
270
+ // De-dupe strategy: key points by *float32 Mercator XY*.
271
+ // This aligns CPU vertex identity with what the GPU will see after float32 uploads,
272
+ // preventing near-duplicate points from creating skinny / unstable triangles.
273
+ const pointMap = new Map();
272
274
  const allVec3s = [];
273
- const allLongLats = [];
275
+ const allMercatorXY = [];
274
276
  let pointCounter = 0;
275
277
  const stepCount = innerCuts - 1; //
276
278
  const edgePointIndexSets = [new Set(), new Set(), new Set()];
@@ -284,8 +286,18 @@ export function partialTessellation(triangleMeta, limits, innerCuts, calculateRe
284
286
  * Adds a point to the vertex arrays if it doesn't already exist.
285
287
  * Returns the index of the point (new or existing).
286
288
  */
289
+ const _f32 = /*@__PURE__*/ new Float32Array(1);
290
+ const _u32 = /*@__PURE__*/ new Uint32Array(_f32.buffer);
291
+ const float32Bits = (n) => {
292
+ _f32[0] = Math.fround(n);
293
+ return _u32[0];
294
+ };
287
295
  const addPoint = (longLat, vec3, edgeIndex) => {
288
- const key = `${Math.round(longLat[0] * 1e8)}|${Math.round(longLat[1] * 1e8)}`;
296
+ const xy = radianToMercatorXY(longLat);
297
+ const x = Math.fround(xy[0]);
298
+ const y = Math.fround(xy[1]);
299
+ // Use float32 bit patterns for an exact, stable key.
300
+ const key = `${float32Bits(x)},${float32Bits(y)}`;
289
301
  let index = pointMap.get(key);
290
302
  if (index !== undefined) {
291
303
  if (edgeIndex !== undefined) {
@@ -296,7 +308,7 @@ export function partialTessellation(triangleMeta, limits, innerCuts, calculateRe
296
308
  // Point is new, add it
297
309
  const v3 = vec3 || createUnitVectorFromLongLat(longLat);
298
310
  allVec3s.push(v3[0], v3[1], v3[2]);
299
- allLongLats.push(longLat[0], longLat[1]);
311
+ allMercatorXY.push(x, y);
300
312
  index = pointCounter++;
301
313
  pointMap.set(key, index);
302
314
  if (edgeIndex !== undefined) {
@@ -415,19 +427,16 @@ export function partialTessellation(triangleMeta, limits, innerCuts, calculateRe
415
427
  realEdgeArcIndices: null,
416
428
  };
417
429
  }
418
- const delaunator = new Delaunator(allLongLats);
430
+ // Triangulate in the same 2D space we render in (Mercator XY).
431
+ const delaunator = new Delaunator(allMercatorXY);
419
432
  let indices = delaunator.triangles;
420
433
  indices = filterEdgePointTriangles(indices, edgePointIndexSets);
421
- // Convert to Mercator
422
- for (let i = 0; i < allLongLats.length / 2; i++) {
423
- const xy = radianToMercatorXY([allLongLats[i * 2], allLongLats[i * 2 + 1]]);
424
- allLongLats[i * 2] = xy[0];
425
- allLongLats[i * 2 + 1] = xy[1];
426
- }
427
- const realEdgeArcIndices = calculateRealEdgeArc ? orderIndcecesAlongArc(triangleMeta, edgePointIndexSets, allLongLats) : null;
434
+ const realEdgeArcIndices = calculateRealEdgeArc
435
+ ? orderIndcecesAlongArc(triangleMeta, edgePointIndexSets, allMercatorXY)
436
+ : null;
428
437
  return {
429
438
  vec3s: new Float32Array(allVec3s),
430
- longLats: new Float32Array(allLongLats),
439
+ longLats: new Float32Array(allMercatorXY),
431
440
  indices: indices,
432
441
  realEdgeArcIndices,
433
442
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pirireis/webglobeplugins",
3
- "version": "1.1.8",
3
+ "version": "1.1.9",
4
4
  "main": "index.js",
5
5
  "author": "Toprak Nihat Deniz Ozturk",
6
6
  "license": "MIT",
@@ -22,7 +22,7 @@ const styleBlockManager = new UniformBlockManager('Style', [
22
22
  { name: "opacity", type: "float", value: new Float32Array([1.0]) },
23
23
  { name: "private_isPickedOn", type: "float", value: new Float32Array([0]) },
24
24
  { name: "private_pickedIndex", type: "float", value: new Float32Array([0]) },
25
- { name: "dontUseColor", type: "float", value: new Float32Array([0]) },
25
+ { name: "useDefaultColor", type: "float", value: new Float32Array([0]) },
26
26
  ], uniformBindingPoints.style);
27
27
  const vertexShaderSource = `#version 300 es
28
28
  #pragma vscode_glsllint_stage : vert
@@ -62,7 +62,7 @@ void main() {
62
62
  );
63
63
 
64
64
  // a_color.a == 255u is reserved as "no per-vertex color" sentinel.
65
- v_color = (a_color.a == 255u || dontUseColor == 1.0) ? defaultColor : decodedColor;
65
+ v_color = (a_color.a == 255u || useDefaultColor == 1.0) ? defaultColor : decodedColor;
66
66
 
67
67
  if (is3D == true) {
68
68
  for (int i = 0; i < 6; i++) {
@@ -1,4 +1,4 @@
1
- function geoJSONToTerrainPolygon(geojson, keyAdapter, colorAdapter) {
1
+ function geoJSONToTerrainPolygon(geojson, keyAdapter, colorAdapter = (feature) => undefined, propertiesAdapter = (feature) => undefined) {
2
2
  if (geojson.type !== 'FeatureCollection') {
3
3
  throw new Error('Invalid GeoJSON: Expected FeatureCollection');
4
4
  }
@@ -8,6 +8,7 @@ function geoJSONToTerrainPolygon(geojson, keyAdapter, colorAdapter) {
8
8
  const geometry = feature.geometry;
9
9
  const key = keyAdapter(feature);
10
10
  const color = colorAdapter(feature);
11
+ const properties = propertiesAdapter(feature);
11
12
  const geometries = [];
12
13
  if (geometry.type === 'Polygon') {
13
14
  const coordinates = geometry.coordinates;
@@ -47,7 +48,7 @@ function geoJSONToTerrainPolygon(geojson, keyAdapter, colorAdapter) {
47
48
  continue; // Skip non-polygon geometries
48
49
  }
49
50
  if (geometries.length > 0) {
50
- polygons.push({ key, color, geometry: geometries });
51
+ polygons.push({ key, color, properties, geometry: geometries });
51
52
  }
52
53
  }
53
54
  return polygons;
@@ -9,9 +9,6 @@
9
9
  *
10
10
  */
11
11
  import earcut from "earcut";
12
- function _isReal(a, b, len) {
13
- return (Math.abs(a - b) === 1 || Math.abs(a - b) === len);
14
- }
15
12
  function edgeKey(a, b) {
16
13
  // undirected edge key
17
14
  return a < b ? `${a},${b}` : `${b},${a}`;
@@ -83,7 +83,7 @@ export class TerrainPolygonSemiPlugin {
83
83
  if (this._options.drawEdges) {
84
84
  this._uboForRealEdgeArcs = this._program.createUBO();
85
85
  this._uboForRealEdgeArcs.updateSingle("defaultColor", new Float32Array([1, 1, 0, 1]));
86
- this._uboForRealEdgeArcs.updateSingle("dontUseColor", new Float32Array([1]));
86
+ this._uboForRealEdgeArcs.updateSingle("useDefaultColor", new Float32Array([1]));
87
87
  this._drawRealEdgeArcs.elementBuffer = gl.createBuffer();
88
88
  }
89
89
  if (this._options.variativeColorsOn) {
@@ -132,7 +132,7 @@ export class TerrainPolygonSemiPlugin {
132
132
  this._workerContact.deleteBulk(keys);
133
133
  this._indexPolygonMap.deleteBulk(keys);
134
134
  }
135
- setUniform(name, value) {
135
+ setUniform(name, value, target = "polygon") {
136
136
  // check options have the key name
137
137
  if (name.startsWith("private_")) {
138
138
  throw new Error(`Cannot set private uniform ${name}`);
@@ -143,7 +143,15 @@ export class TerrainPolygonSemiPlugin {
143
143
  }
144
144
  this._options[name] = value;
145
145
  }
146
- this._uboHandler.updateSingle(name, value);
146
+ if (target === "polygon") {
147
+ this._uboHandler.updateSingle(name, value);
148
+ }
149
+ else if (target === "edgeArc") {
150
+ this._uboForRealEdgeArcs?.updateSingle(name, value);
151
+ }
152
+ else {
153
+ console.warn(`Unknown target ${target} for setUniform, must be "polygon" or "edgeArc"`);
154
+ }
147
155
  this.globe.DrawRender();
148
156
  }
149
157
  setOpacity(opacity) {
@@ -357,5 +365,8 @@ function getGlStates(gl) {
357
365
  };
358
366
  }
359
367
  function roundTo7(n) {
360
- return Math.round(n * 1e7) / 1e7;
368
+ // Keep at most 7 digits after the decimal (degrees), then quantize to float32
369
+ // to match GPU attribute precision and make identity comparisons stable.
370
+ const rounded = Math.round(n * 1e7) / 1e7;
371
+ return Math.fround(rounded);
361
372
  }
@@ -1,299 +0,0 @@
1
- /**
2
- * IconData;
3
- * iconMap, iconCoords for objectArray
4
- *
5
- * Pin data:
6
- * long, lat, payload
7
- *
8
- * methods insertPin pin and its data
9
- * methods deletePin
10
- *
11
- * Behaviur injection:
12
- * hover
13
- * move
14
- * pick drop
15
- * mouseclicks
16
- * keyboard clicks
17
- * mouse curser changes ( this can be implemented inside above methods)
18
- */
19
- import { CSGlobe, CSIconTypes, CSObjectTypes, CSObjectArrayUpdateTypes, } from "@pirireis/webglobe";
20
- /**
21
- * @typedef {Object} IconPayload
22
- * @property {string} app6DCode
23
- * @property {string} accsCode
24
- */
25
- /**
26
- * @typedef {IconPayload & Object.<string, any>} PointPayload
27
- */
28
- /**
29
- * @typedef {Object} CanChangeOptions
30
- * @property {boolean} [attribs=false]
31
- * @property {boolean} [icon=false]
32
- * @property {boolean} [label=false]
33
- * @property {boolean} [heading=false]
34
- */
35
- /**
36
- * @typedef {Object} PointObjectArray
37
- * @property {number[]} coords [longitude1, latitude1, longitude2, latitude2, ....]
38
- * @property {number[]} coordsZ [z1, z2, ....]
39
- * @property {PointPayload[]} attribs
40
- */
41
- function addIconMap(globe, mapName = "IconMapName", ICON_MAP_URL, ICON_MAP_JSON_URL) {
42
- globe.api_AddIconMap('IconMapName', ICON_MAP_URL, ICON_MAP_JSON_URL);
43
- }
44
- class ObjectArray {
45
- constructor(symbolSet, style) {
46
- this.symbolSet = symbolSet;
47
- this.style = style;
48
- }
49
- setSymbolSet(symbolSet) {
50
- this.symbolSet = symbolSet;
51
- }
52
- }
53
- class PinPointObjectArray {
54
- constructor(id, globe, symbolSet, // iconMapName
55
- style, callBacks = {
56
- mouseDown: (pinItem, event) => { },
57
- mouseMove: (pinItem, event) => { },
58
- mouseUp: (pinItem, event) => { },
59
- mouseClick: (pinItem, event) => { },
60
- mouseDblClick: (pinItem, event) => { },
61
- } = {}) {
62
- this.id = id;
63
- this.globe = globe;
64
- this.symbolSet = symbolSet;
65
- this.style = style;
66
- this.iconMapName = iconMapName;
67
- this.callBacks = callBacks;
68
- // this item will be used
69
- this.currentItem = null;
70
- }
71
- insertPins(pins) {
72
- }
73
- deletePins(pinsIDs) {
74
- const deleteBucket = {
75
- coords: [],
76
- coordsZ: [],
77
- attribs: []
78
- };
79
- }
80
- _queryByScreenFindFirst() {
81
- const { clientX, clientY } = this.globe.api_GetMousePos();
82
- const allObjects = this.globe.queryByScreen(clientX, clientY);
83
- for (let i = 0; i < allObjects.length; i++) {
84
- const { obj, owner } = allObjects[i];
85
- if (owner === this.objectArray) {
86
- return obj;
87
- }
88
- }
89
- }
90
- // mouseDown(x, y, event) {
91
- // return false
92
- // }
93
- // // mouse'a basılıp hareket ettirildiğinde, mouseDown'dan true dönmüşse çağrılır
94
- // mouseMove(x, y, event) {
95
- // }
96
- // // mouse bırakıldığında çağrılır
97
- // mouseUp(x, y, event) {
98
- // }
99
- // // harita üzerinde tıklandığında çağrılır
100
- // mouseClick() {
101
- // return false
102
- // }
103
- // // harita üzerinde çift tıklandığında çağrılır
104
- // mouseDblClick() {
105
- // return false
106
- // }
107
- free() {
108
- this.globe.api_ClearMouseEvents(); // TODO
109
- }
110
- _setMouseEvent() {
111
- const { globe } = this;
112
- globe.api_SetMouseEvents();
113
- }
114
- _getIdForGlobe() {
115
- const { globe, objectArray } = this;
116
- const callback = () => {
117
- const allObjects = globe.api_queryByScreen();
118
- };
119
- return `${this.id}_pinObjectArrayMouseEvent`;
120
- }
121
- }
122
- class ObjectArrayLabels {
123
- /**
124
- * @param {number | string} id
125
- * @param {CSGlobe} globe
126
- */
127
- constructor(id, globe, labelStyle = null) {
128
- this.id = id;
129
- this.setGlobe(globe);
130
- this.data = [
131
- {
132
- coords: [],
133
- coordsZ: [],
134
- attribs: [],
135
- },
136
- ];
137
- this.primaryKey = "id";
138
- this.objectType = CSObjectTypes.POINT;
139
- this.filter = null;
140
- this.bbox = null;
141
- this.startLod = 2;
142
- this.endLod = 19;
143
- this.query = true;
144
- this.reportObj = function (values, mouseEvent) { };
145
- this._idCollector = new Set();
146
- this._setStyle(labelStyle);
147
- }
148
- _setStyle(labelStyle) {
149
- this.style = this.globe.ObjectArray.GetDefaultStyle();
150
- if (labelStyle) {
151
- this.style.labels[0] = labelStyle;
152
- }
153
- else {
154
- this.style.labels[0].vAlignment = 2; // dikey olarak tam noktanın ortası
155
- this.style.labels[0].hAlignment = 2; // yatay olarak tam noktanın ortası
156
- this.style.labels[0].size = 11;
157
- this.style.labels[0].drawAlways = true;
158
- this.style.labels[0].fontFamily.name = "arial";
159
- this.style.labels[0].fontFamily.bold = false;
160
- this.style.labels[0].fontFamily.hollow = true;
161
- this.style.labels[0].fontFamily.hollowWidth = 1;
162
- this.style.labels[0].fontFamily.hollowOpacity = 1;
163
- }
164
- this.style.labels[0].text = "${value}";
165
- this.style.fidKey = this.primaryKey;
166
- this.style.iconType = CSIconTypes.NOICON; // milIcon ekleyebilmek için icon tipi MAP olmalı
167
- }
168
- /**
169
- * @param {CSGlobe} [globe]
170
- */
171
- setGlobe(globe) {
172
- if (!globe)
173
- return;
174
- this.globe = globe;
175
- }
176
- addToMap() {
177
- this.globe.ObjectArray.Add(this);
178
- }
179
- removeFromMap() {
180
- this.flush();
181
- this.globe.ObjectArray.Delete(this.id);
182
- }
183
- /**
184
- * Haritaya kullanılacak veriyi verir. Her çağrıldığında tüm hesaplamalar yeniden yapılır
185
- * @param {PointObjectArray} data
186
- */
187
- setData(data) {
188
- this.globe.ObjectArray.SetData(this, [data]);
189
- }
190
- /**
191
- * @param {PointObjectArray} data
192
- * @param {CSObjectArrayUpdateTypes} operation
193
- * @param {CanChangeOptions} [canChange]
194
- */
195
- updateData(data, operation, canChange) {
196
- this.globe.ObjectArray.UpdateData(this, operation, [data], canChange);
197
- }
198
- flush() {
199
- const deleteBucket = {
200
- coords: [],
201
- coordsZ: [],
202
- attribs: [],
203
- };
204
- this.setData(deleteBucket);
205
- this._idCollector = new Set();
206
- }
207
- /**
208
- * Haritaya kullanılacak veriyi verir. Her çağrıldığında tüm hesaplamalar yeniden yapılmaz, ancak ön hesaplama maliyeti vardır
209
- * @param {PointObjectArray} data
210
- */
211
- setControlledData(data) {
212
- const paritalData = {
213
- add: {
214
- coords: [],
215
- coordsZ: [],
216
- attribs: [],
217
- },
218
- update: {
219
- coords: [],
220
- coordsZ: [],
221
- attribs: [],
222
- },
223
- delete: {
224
- coords: [],
225
- coordsZ: [],
226
- attribs: [],
227
- },
228
- canChange: {
229
- // Tüm değişimler gözardı edilecek
230
- attribs: false,
231
- icon: false,
232
- label: false,
233
- heading: false,
234
- },
235
- };
236
- // İlk toplu ekleme adımı
237
- if (this._idCollector.size === 0) {
238
- this.setData(data);
239
- // Eklenen id'ler set edildi
240
- for (let i = 0; i < data.attribs.length; i++) {
241
- const currentPayload = data.attribs[i];
242
- const id = currentPayload[this.primaryKey];
243
- this._idCollector.add(id);
244
- }
245
- return;
246
- }
247
- /** @type {Set<string>} */
248
- const dataIds = new Set();
249
- for (let i = 0; i < data.attribs.length; i++) {
250
- const currentPayload = data.attribs[i];
251
- const id = currentPayload[this.primaryKey];
252
- let bucket = paritalData.add;
253
- dataIds.add(id);
254
- if (this._idCollector.has(id)) {
255
- bucket = paritalData.update;
256
- }
257
- else {
258
- this._idCollector.add(id);
259
- }
260
- bucket.coords.push(data.coords[2 * i], data.coords[2 * i + 1]);
261
- bucket.coordsZ.push(data.coordsZ[i]);
262
- bucket.attribs.push(currentPayload);
263
- }
264
- // Artık varolmayan elemenları sil
265
- const deleteBucket = paritalData.delete;
266
- this._idCollector.forEach((id) => {
267
- if (!dataIds.has(id)) {
268
- this._idCollector.delete(id);
269
- deleteBucket.coords.push(0, 0);
270
- deleteBucket.coordsZ.push(0);
271
- deleteBucket.attribs.push({
272
- [this.primaryKey]: id,
273
- });
274
- }
275
- });
276
- if (paritalData.add.coords.length > 0) {
277
- this.updateData(paritalData.add, CSObjectArrayUpdateTypes.ADD);
278
- }
279
- if (paritalData.update.coords.length > 0) {
280
- this.updateData(paritalData.update, CSObjectArrayUpdateTypes.UPDATE, paritalData.canChange);
281
- }
282
- if (paritalData.delete.coords.length > 0) {
283
- this.updateData(paritalData.delete, CSObjectArrayUpdateTypes.DELETE);
284
- }
285
- }
286
- setPrimarykey(key) {
287
- this.primaryKey = key;
288
- }
289
- setSymbolSet(symbolSet) {
290
- this.symbolSet = symbolSet;
291
- }
292
- getLabelStyle() {
293
- return this.style.labels[0];
294
- }
295
- setLabelStyle(style) {
296
- this.style.labels[0] = style;
297
- }
298
- }
299
- export default ObjectArrayLabels;
@@ -1,60 +0,0 @@
1
- "use strict";
2
- /**
3
- * This class registered as globe plugin one for each globe. to the head of plugin call stack.
4
- */
5
- class PinPointTotem {
6
- constructor(globe) {
7
- this.objectArrayMap = new Map();
8
- }
9
- /**
10
- * objectArray
11
- * callbacks ={
12
- * mouseDown,
13
- * mouseMove,
14
- * mouseUp,
15
- * mouseClick,
16
- * mouseDbClick,
17
- * keyDown,
18
- * keyUp
19
- * }
20
- */
21
- registerPinMap(objectArray, callbacks) {
22
- this.objectArrayMap.set(objectArray, callbacks);
23
- }
24
- unregisterPinMap(objectArray) {
25
- if (this.objectArrayMap.has(objectArray)) {
26
- this.objectArrayMap.delete(objectArray);
27
- }
28
- else {
29
- console.warn('PinPointTotem objectArrayMap does not contain the objectArray');
30
- }
31
- }
32
- // GlobeMethods
33
- // haritada sol butona basıldığında çağrılır
34
- mouseDown(x, y, event) {
35
- return false;
36
- }
37
- // mouse'a basılıp hareket ettirildiğinde, mouseDown'dan true dönmüşse çağrılır
38
- mouseMove(x, y, event) {
39
- }
40
- // mouse up'ın left'i mouseDown'dan true dönmüşse çağrılır, edit mode içindir
41
- // right'i ise sağ tıka basılıp bırakıldığında çağrılır
42
- mouseUp(x, y, event) {
43
- }
44
- // harita üzerinde tıklandığında çağrılır
45
- mouseClick(x, y, event) {
46
- return false;
47
- }
48
- // harita üzerinde çift tıklandığında çağrılır
49
- mouseDblClick(x, y, event) {
50
- return false;
51
- }
52
- // klavyeden bir tuşa basıldığı anda ve tuşa basılı kalınmaya devam edildiği durumlarda çalışır
53
- keyDown(event) {
54
- return false;
55
- }
56
- // klavyedeki bir tuştan parmak çekildiği anda çalışır
57
- keyUp(event) {
58
- return false;
59
- }
60
- }