geojs 1.6.3 → 1.7.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/README.md +14 -9
  3. package/geo.js +1101 -417
  4. package/geo.lean.js +1101 -417
  5. package/geo.lean.min.js +3 -3
  6. package/geo.min.js +5 -5
  7. package/package.json +6 -8
  8. package/src/annotation.js +11 -7
  9. package/src/annotationLayer.js +7 -7
  10. package/src/camera.js +5 -5
  11. package/src/canvas/pixelmapFeature.js +208 -0
  12. package/src/choroplethFeature.js +1 -1
  13. package/src/domRenderer.js +2 -1
  14. package/src/featureLayer.js +1 -1
  15. package/src/fileReader.js +2 -2
  16. package/src/imageTile.js +2 -1
  17. package/src/index.js +1 -0
  18. package/src/isolineFeature.js +1 -1
  19. package/src/layer.js +4 -2
  20. package/src/lineFeature.js +3 -3
  21. package/src/map.js +17 -15
  22. package/src/mapInteractor.js +17 -17
  23. package/src/markerFeature.js +2 -2
  24. package/src/meshFeature.js +1 -1
  25. package/src/osmLayer.js +2 -0
  26. package/src/pixelmapFeature.js +97 -249
  27. package/src/pixelmapLayer.js +145 -0
  28. package/src/pointFeature.js +1 -1
  29. package/src/polygonFeature.js +3 -3
  30. package/src/quadFeature.js +1 -1
  31. package/src/registry.js +2 -2
  32. package/src/svg/svgRenderer.js +3 -3
  33. package/src/tileCache.js +1 -1
  34. package/src/tileLayer.js +13 -12
  35. package/src/trackFeature.js +19 -19
  36. package/src/transform.js +8 -9
  37. package/src/typedef.js +2 -0
  38. package/src/ui/sliderWidget.js +1 -1
  39. package/src/util/clustering.js +3 -3
  40. package/src/util/color.js +1 -1
  41. package/src/util/common.js +7 -7
  42. package/src/util/throttle.js +1 -1
  43. package/src/webgl/index.js +2 -0
  44. package/src/webgl/lookupTable2D.js +122 -0
  45. package/src/webgl/markerFeature.js +1 -1
  46. package/src/webgl/pixelmapFeature.frag +47 -0
  47. package/src/webgl/pixelmapFeature.js +203 -0
  48. package/src/webgl/pointFeature.js +1 -1
  49. package/src/webgl/quadFeature.js +35 -2
  50. package/src/webgl/webglRenderer.js +1 -0
@@ -0,0 +1,203 @@
1
+ var inherit = require('../inherit');
2
+ var registerFeature = require('../registry').registerFeature;
3
+ var pixelmapFeature = require('../pixelmapFeature');
4
+ var lookupTable2D = require('./lookupTable2D');
5
+ var util = require('../util');
6
+
7
+ /**
8
+ * Create a new instance of class webgl.pixelmapFeature.
9
+ *
10
+ * @class
11
+ * @alias geo.webgl.pixelmapFeature
12
+ * @extends geo.pixelmapFeature
13
+ * @param {geo.pixelmapFeature.spec} arg
14
+ * @returns {geo.webgl.pixelmapFeature}
15
+ */
16
+ var webgl_pixelmapFeature = function (arg) {
17
+ 'use strict';
18
+
19
+ if (!(this instanceof webgl_pixelmapFeature)) {
20
+ return new webgl_pixelmapFeature(arg);
21
+ }
22
+ pixelmapFeature.call(this, arg);
23
+
24
+ var object = require('./object');
25
+ object.call(this);
26
+
27
+ const vgl = require('vgl');
28
+ const fragmentShader = require('./pixelmapFeature.frag');
29
+
30
+ var m_quadFeature,
31
+ m_quadFeatureInit,
32
+ s_exit = this._exit,
33
+ m_lookupTable,
34
+ m_this = this;
35
+
36
+ /**
37
+ * If the specified coordinates are in the rendered quad, use the basis
38
+ * information from the quad to determine the pixelmap index value so that it
39
+ * can be included in the `found` results.
40
+ *
41
+ * @param {geo.geoPosition} geo Coordinate.
42
+ * @param {string|geo.transform|null} [gcs] Input gcs. `undefined` to use
43
+ * the interface gcs, `null` to use the map gcs, or any other transform.
44
+ * @returns {geo.feature.searchResult} An object with a list of features and
45
+ * feature indices that are located at the specified point.
46
+ */
47
+ this.pointSearch = function (geo, gcs) {
48
+ if (m_quadFeature && m_this.m_info) {
49
+ let result = m_quadFeature.pointSearch(geo, gcs);
50
+ // use the last index by preference, since for tile layers, this is the
51
+ // topmosttile
52
+ let idxIdx = result.index.length - 1;
53
+ for (; idxIdx >= 0; idxIdx -= 1) {
54
+ if (result.extra[result.index[idxIdx]]._quad &&
55
+ result.extra[result.index[idxIdx]]._quad.image) {
56
+ const img = result.extra[result.index[idxIdx]]._quad.image;
57
+ const basis = result.extra[result.index[idxIdx]].basis;
58
+ const x = Math.floor(basis.x * img.width);
59
+ const y = Math.floor(basis.y * img.height);
60
+ const canvas = document.createElement('canvas');
61
+ canvas.width = canvas.height = 1;
62
+ const context = canvas.getContext('2d');
63
+ context.drawImage(img, x, y, 1, 1, 0, 0, 1, 1);
64
+ const pixel = context.getImageData(0, 0, 1, 1).data;
65
+ const idx = pixel[0] + pixel[1] * 256 + pixel[2] * 256 * 256;
66
+ result = {
67
+ index: [idx],
68
+ found: [m_this.data()[idx]]
69
+ };
70
+ return result;
71
+ }
72
+ }
73
+ }
74
+ return {index: [], found: []};
75
+ };
76
+
77
+ /**
78
+ * Given the loaded pixelmap image, create a texture for the colors and a
79
+ * quad that will use it.
80
+ */
81
+ this._computePixelmap = function () {
82
+ var data = m_this.data() || [],
83
+ colorFunc = m_this.style.get('color');
84
+
85
+ let indexRange = m_this.indexModified(undefined, 'clear');
86
+ let fullUpdate = m_this.dataTime().timestamp() >= m_this.buildTime().timestamp() || indexRange === undefined;
87
+ if (!m_lookupTable) {
88
+ m_lookupTable = lookupTable2D();
89
+ m_lookupTable.setTextureUnit(1);
90
+ fullUpdate = true;
91
+ }
92
+ let clrLen = Math.max(1, data.length);
93
+ const maxWidth = m_lookupTable.maxWidth();
94
+ if (clrLen > maxWidth && clrLen % maxWidth) {
95
+ clrLen += maxWidth - (clrLen % maxWidth);
96
+ }
97
+
98
+ let colors;
99
+ if (!fullUpdate) {
100
+ colors = m_lookupTable.colorTable();
101
+ fullUpdate = colors.length !== clrLen * 4;
102
+ indexRange[0] = Math.max(0, indexRange[0]);
103
+ indexRange[1] = Math.min(data.length, indexRange[1] + 1);
104
+ }
105
+ if (fullUpdate) {
106
+ colors = new Uint8Array(clrLen * 4);
107
+ indexRange = [0, data.length];
108
+ }
109
+ for (let i = indexRange[0]; i < indexRange[1]; i += 1) {
110
+ const d = data[i];
111
+ const color = util.convertColor(colorFunc.call(m_this, d, i));
112
+ colors[i * 4] = color.r * 255;
113
+ colors[i * 4 + 1] = color.g * 255;
114
+ colors[i * 4 + 2] = color.b * 255;
115
+ colors[i * 4 + 3] = color.a === undefined ? 255 : (color.a * 255);
116
+ }
117
+ m_this.m_info = {colors: colors};
118
+ // check if colors haven't changed
119
+ var oldcolors = m_lookupTable.colorTable();
120
+ if (oldcolors && oldcolors.length === colors.length) {
121
+ let idx = indexRange[0] * 4;
122
+ for (; idx < indexRange[1] * 4; idx += 1) {
123
+ if (colors[idx] !== oldcolors[idx]) {
124
+ break;
125
+ }
126
+ }
127
+ if (idx === indexRange[1] * 4) {
128
+ return;
129
+ }
130
+ }
131
+ m_lookupTable.colorTable(colors);
132
+ /* If we haven't made a quad feature, make one now */
133
+ if (!m_quadFeature) {
134
+ m_quadFeature = m_this.layer().createFeature('quad', {
135
+ selectionAPI: false,
136
+ gcs: m_this.gcs(),
137
+ visible: m_this.visible(undefined, true)
138
+ });
139
+ m_quadFeatureInit = false;
140
+ }
141
+ if (!m_quadFeatureInit) {
142
+ m_this.dependentFeatures([m_quadFeature]);
143
+ m_quadFeature.setShader('image_fragment', fragmentShader);
144
+ m_quadFeature._hookBuild = (prog) => {
145
+ const lutSampler = new vgl.uniform(vgl.GL.INT, 'lutSampler');
146
+ lutSampler.set(m_lookupTable.textureUnit());
147
+ prog.addUniform(lutSampler);
148
+ const lutWidth = new vgl.uniform(vgl.GL.INT, 'lutWidth');
149
+ lutWidth.set(m_lookupTable.width);
150
+ prog.addUniform(lutWidth);
151
+ const lutHeight = new vgl.uniform(vgl.GL.INT, 'lutHeight');
152
+ lutHeight.set(m_lookupTable.height);
153
+ prog.addUniform(lutHeight);
154
+ };
155
+ m_quadFeature._hookRenderImageQuads = (renderState, quads) => {
156
+ quads.forEach((quad) => {
157
+ if (quad.image && quad.texture && !quad.texture.nearestPixel()) {
158
+ quad.texture.setNearestPixel(true);
159
+ }
160
+ });
161
+ m_lookupTable.bind(renderState, quads);
162
+ };
163
+ if (m_quadFeatureInit === false) {
164
+ m_quadFeature.style({
165
+ image: m_this.m_srcImage,
166
+ position: m_this.style.get('position')})
167
+ .data([{}])
168
+ .draw();
169
+ }
170
+ m_quadFeatureInit = true;
171
+ }
172
+ };
173
+
174
+ /**
175
+ * Destroy. Deletes the associated quadFeature.
176
+ *
177
+ * @returns {this}
178
+ */
179
+ this._exit = function () {
180
+ if (m_quadFeature && m_this.layer()) {
181
+ m_this.layer().deleteFeature(m_quadFeature);
182
+ m_quadFeature = null;
183
+ m_this.dependentFeatures([]);
184
+ }
185
+ s_exit();
186
+ return m_this;
187
+ };
188
+
189
+ if (arg.quadFeature) {
190
+ m_quadFeature = arg.quadFeature;
191
+ }
192
+ this._init(arg);
193
+ return this;
194
+ };
195
+
196
+ inherit(webgl_pixelmapFeature, pixelmapFeature);
197
+
198
+ // Now register it
199
+ var capabilities = {};
200
+ capabilities[pixelmapFeature.capabilities.lookup] = true;
201
+
202
+ registerFeature('webgl', 'pixelmap', webgl_pixelmapFeature, capabilities);
203
+ module.exports = webgl_pixelmapFeature;
@@ -78,7 +78,7 @@ var webgl_pointFeature = function (arg) {
78
78
  /**
79
79
  * Create and style the data needed to render the points.
80
80
  *
81
- * @param {boolean} onlyStyle if true, use the existing geometry and just
81
+ * @param {boolean} [onlyStyle] if true, use the existing geometry and just
82
82
  * recalculate the style.
83
83
  */
84
84
  function createGLPoints(onlyStyle) {
@@ -158,7 +158,7 @@ var webgl_quadFeature = function (arg) {
158
158
  * Build this feature.
159
159
  */
160
160
  this._build = function () {
161
- var mapper, mat, prog, srctex, unicrop, unicropsource, geom, context;
161
+ var mapper, mat, prog, srctex, unicrop, unicropsource, geom, context, sampler2d;
162
162
 
163
163
  if (!m_this.position()) {
164
164
  return;
@@ -183,6 +183,10 @@ var webgl_quadFeature = function (arg) {
183
183
  prog.addUniform(new vgl.projectionUniform('projectionMatrix'));
184
184
  prog.addUniform(new vgl.floatUniform('opacity', 1.0));
185
185
  prog.addUniform(new vgl.floatUniform('zOffset', 0.0));
186
+ /* Use texture unit 0 */
187
+ sampler2d = new vgl.uniform(vgl.GL.INT, 'sampler2d');
188
+ sampler2d.set(0);
189
+ prog.addUniform(sampler2d);
186
190
  context = m_this.renderer()._glContext();
187
191
  unicrop = new vgl.uniform(context.FLOAT_VEC2, 'crop');
188
192
  unicrop.set([1.0, 1.0]);
@@ -194,6 +198,9 @@ var webgl_quadFeature = function (arg) {
194
198
  context.VERTEX_SHADER, context, vertexShaderImage));
195
199
  prog.addShader(vgl.getCachedShader(
196
200
  context.FRAGMENT_SHADER, context, fragmentShaderImage));
201
+ if (m_this._hookBuild) {
202
+ m_this._hookBuild(prog);
203
+ }
197
204
  mat.addAttribute(prog);
198
205
  mat.addAttribute(new vgl.blend());
199
206
  /* This is similar to vgl.planeSource */
@@ -355,13 +362,18 @@ var webgl_quadFeature = function (arg) {
355
362
  cropsrc = {x0: 0, y0: 0, x1: 1, y1: 1}, quadcropsrc,
356
363
  w, h, quadw, quadh;
357
364
 
365
+ if (m_this._hookRenderImageQuads) {
366
+ m_this._hookRenderImageQuads(renderState, m_quads.imgQuads);
367
+ }
358
368
  context.bindBuffer(context.ARRAY_BUFFER, m_glBuffers.imgQuadsPosition);
359
369
  $.each(m_quads.imgQuads, function (idx, quad) {
360
370
  if (!quad.image) {
361
371
  return;
362
372
  }
363
373
  quad.texture.bind(renderState);
364
- if (context.getError() === context.OUT_OF_MEMORY) {
374
+ // only check if the context is out of memory when using modestly large
375
+ // textures. The check is slow.
376
+ if ((quad.image.width > 4096 || quad.image.height > 4096 || quad.image.width * quad.image.height > 4194304) && context.getError() === context.OUT_OF_MEMORY) {
365
377
  console.log('Insufficient GPU memory for texture');
366
378
  }
367
379
  if (quad.opacity !== opacity) {
@@ -450,6 +462,27 @@ var webgl_quadFeature = function (arg) {
450
462
  m_this.modified();
451
463
  };
452
464
 
465
+ /**
466
+ * Set the image or color vertex or fragment shader.
467
+ *
468
+ * @param {string} shaderType One of `image_vertex`, `image_fragment`,
469
+ * `color_vertex`, or `color_fragment`.
470
+ * @param {string} shaderCode The shader program.
471
+ * @returns {this} The class instance on success, undefined in an unknown
472
+ * shaderType was specified.
473
+ */
474
+ this.setShader = function (shaderType, shaderCode) {
475
+ switch (shaderType) {
476
+ case 'image_vertex': vertexShaderImage = shaderCode; break;
477
+ case 'image_fragment': fragmentShaderImage = shaderCode; break;
478
+ case 'color_vertex': vertexShaderColor = shaderCode; break;
479
+ case 'color_fragment': fragmentShaderColor = shaderCode; break;
480
+ default:
481
+ return;
482
+ }
483
+ return m_this;
484
+ };
485
+
453
486
  /**
454
487
  * Destroy.
455
488
  */
@@ -319,6 +319,7 @@ webglRenderer.supported = function () {
319
319
  var canvas, ctx, exts; // eslint-disable-line no-unused-vars
320
320
  try {
321
321
  canvas = document.createElement('canvas');
322
+ /** @type {WebGLRenderingContext} */
322
323
  ctx = (canvas.getContext('webgl') ||
323
324
  canvas.getContext('experimental-webgl'));
324
325
  /* getSupportExtensions will throw an exception if the context isn't