corrugatedPoint.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /**
  2. * Created by xdw on 2020/5/9.
  3. */
  4. //需要gl-matrix.js
  5. /*
  6. * popupTemplate={
  7. title: "{NAME}",
  8. content: "Population: {value}."
  9. }
  10. graphicsList=[
  11. {
  12. geometry: {
  13. type: "point",
  14. x: -80,
  15. y: 35
  16. },
  17. attributes: {
  18. NAME: "Los Angeles",
  19. value: 3792621
  20. }
  21. }
  22. ]
  23. *
  24. *
  25. * */
  26. var corrugatedPointlayer;
  27. function createCorrugatedPoint (popupTemplate,graphicsList,map) {
  28. require([
  29. "esri/core/watchUtils",
  30. "esri/core/promiseUtils",
  31. "esri/geometry/support/webMercatorUtils",
  32. "esri/layers/GraphicsLayer",
  33. "esri/views/2d/layers/BaseLayerViewGL2D"
  34. ], function(
  35. watchUtils,
  36. promiseUtils,
  37. webMercatorUtils,
  38. GraphicsLayer,
  39. BaseLayerViewGL2D
  40. ) {
  41. m_map.remove(corrugatedPointlayer);
  42. // Subclass the custom layer view from BaseLayerViewGL2D.
  43. var CustomLayerView2D = BaseLayerViewGL2D.createSubclass({
  44. // Locations of the two vertex attributes that we use. They will be bound to the shader program before linking.
  45. aPosition: 0,
  46. aOffset: 1,
  47. constructor: function() {
  48. // Geometrical transformations that must be recomputed from scratch at every frame.
  49. this.transform = mat3.create();
  50. this.translationToCenter = vec2.create();
  51. this.screenTranslation = vec2.create();
  52. // Geometrical transformations whose only a few elements must be updated per frame. Those elements are marked with NaN.
  53. this.display = mat3.fromValues(NaN, 0, 0, 0, NaN, 0, -1, 1, 1);
  54. this.screenScaling = vec3.fromValues(NaN, NaN, 1);
  55. // Whether the vertex and index buffers need to be updated due to a change in the layer data.
  56. this.needsUpdate = false;
  57. // We listen for changes to the graphics collection of the layer
  58. // and trigger the generation of new frames. A frame rendered while
  59. // `needsUpdate` is true may cause an update of the vertex and
  60. // index buffers.
  61. var requestUpdate = function() {
  62. this.needsUpdate = true;
  63. this.requestRender();
  64. }.bind(this);
  65. this.watcher = watchUtils.on(
  66. this,
  67. "layer.graphics",
  68. "change",
  69. requestUpdate,
  70. requestUpdate,
  71. requestUpdate
  72. );
  73. },
  74. // Called once a custom layer is added to the map.layers collection and this layer view is instantiated.
  75. attach: function() {
  76. var gl = this.context;
  77. // Define and compile shaders.
  78. var vertexSource =
  79. "precision highp float;" +
  80. "uniform mat3 u_transform;" +
  81. "uniform mat3 u_display;" +
  82. "attribute vec2 a_position;" +
  83. "attribute vec2 a_offset;" +
  84. "varying vec2 v_offset;" +
  85. "const float SIZE = 100.0;" +//直径
  86. "void main() {" +
  87. " gl_Position.xy = (u_display * (u_transform * vec3(a_position, 1.0) + vec3(a_offset * SIZE, 0.0))).xy;" +
  88. " gl_Position.zw = vec2(0.0, 1.0);" +
  89. " v_offset = a_offset;" +
  90. "}";
  91. var fragmentSource =
  92. "precision highp float;" +
  93. "uniform float u_current_time;" +
  94. "varying vec2 v_offset;" +
  95. "const float PI = 3.14159;" +
  96. "const float N_RINGS = 3.0;" +
  97. "const vec3 COLOR = vec3(0.23, 0.43, 0.70);" +//颜色修改
  98. "const float FREQ = 1.0;" +//频率
  99. "void main() {" +
  100. " float l = length(v_offset);" +
  101. " float intensity = clamp(cos(l * PI), 0.0, 1.0) * clamp(cos(2.0 * PI * (l * 2.0 * N_RINGS - FREQ * u_current_time)), 0.0, 1.0);" +
  102. " gl_FragColor = vec4(COLOR * intensity, intensity);" +
  103. "}";
  104. var vertexShader = gl.createShader(gl.VERTEX_SHADER);
  105. gl.shaderSource(vertexShader, vertexSource);
  106. gl.compileShader(vertexShader);
  107. var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
  108. gl.shaderSource(fragmentShader, fragmentSource);
  109. gl.compileShader(fragmentShader);
  110. // Create the shader program.
  111. this.program = gl.createProgram();
  112. gl.attachShader(this.program, vertexShader);
  113. gl.attachShader(this.program, fragmentShader);
  114. // Bind attributes.
  115. gl.bindAttribLocation(this.program, this.aPosition, "a_position");
  116. gl.bindAttribLocation(this.program, this.aOffset, "a_offset");
  117. // Link.
  118. gl.linkProgram(this.program);
  119. // Shader objects are not needed anymore.
  120. gl.deleteShader(vertexShader);
  121. gl.deleteShader(fragmentShader);
  122. // Retrieve uniform locations once and for all.
  123. this.uTransform = gl.getUniformLocation(
  124. this.program,
  125. "u_transform"
  126. );
  127. this.uDisplay = gl.getUniformLocation(this.program, "u_display");
  128. this.uCurrentTime = gl.getUniformLocation(
  129. this.program,
  130. "u_current_time"
  131. );
  132. // Create the vertex and index buffer. They are initially empty. We need to track the
  133. // size of the index buffer because we use indexed drawing.
  134. this.vertexBuffer = gl.createBuffer();
  135. this.indexBuffer = gl.createBuffer();
  136. // Number of indices in the index buffer.
  137. this.indexBufferSize = 0;
  138. // When certain conditions occur, we update the buffers and re-compute and re-encode
  139. // all the attributes. When buffer update occurs, we also take note of the current center
  140. // of the view state, and we reset a vector called `translationToCenter` to [0, 0], meaning that the
  141. // current center is the same as it was when the attributes were recomputed.
  142. this.centerAtLastUpdate = vec2.fromValues(
  143. this.view.state.center[0],
  144. this.view.state.center[1]
  145. );
  146. },
  147. // Called once a custom layer is removed from the map.layers collection and this layer view is destroyed.
  148. detach: function() {
  149. // Stop watching the `layer.graphics` collection.
  150. this.watcher.remove();
  151. var gl = this.context;
  152. // Delete buffers and programs.
  153. gl.deleteBuffer(this.vertexBuffer);
  154. gl.deleteBuffer(this.indexBuffer);
  155. gl.deleteProgram(this.program);
  156. },
  157. // Called every time a frame is rendered.
  158. render: function(renderParameters) {
  159. var gl = renderParameters.context;
  160. var state = renderParameters.state;
  161. // Update vertex positions. This may trigger an update of
  162. // the vertex coordinates contained in the vertex buffer.
  163. // There are three kinds of updates:
  164. // - Modification of the layer.graphics collection ==> Buffer update
  165. // - The view state becomes non-stationary ==> Only view update, no buffer update
  166. // - The view state becomes stationary ==> Buffer update
  167. this.updatePositions(renderParameters);
  168. // If there is nothing to render we return.
  169. if (this.indexBufferSize === 0) {
  170. return;
  171. }
  172. // Update view `transform` matrix; it converts from map units to pixels.
  173. mat3.identity(this.transform);
  174. this.screenTranslation[0] = (state.pixelRatio * state.size[0]) / 2;
  175. this.screenTranslation[1] = (state.pixelRatio * state.size[1]) / 2;
  176. mat3.translate(
  177. this.transform,
  178. this.transform,
  179. this.screenTranslation
  180. );
  181. mat3.rotate(
  182. this.transform,
  183. this.transform,
  184. (Math.PI * state.rotation) / 180
  185. );
  186. this.screenScaling[0] = state.pixelRatio / state.resolution;
  187. this.screenScaling[1] = -state.pixelRatio / state.resolution;
  188. mat3.scale(this.transform, this.transform, this.screenScaling);
  189. mat3.translate(
  190. this.transform,
  191. this.transform,
  192. this.translationToCenter
  193. );
  194. // Update view `display` matrix; it converts from pixels to normalized device coordinates.
  195. this.display[0] = 2 / (state.pixelRatio * state.size[0]);
  196. this.display[4] = -2 / (state.pixelRatio * state.size[1]);
  197. // Draw.
  198. gl.useProgram(this.program);
  199. gl.uniformMatrix3fv(this.uTransform, false, this.transform);
  200. gl.uniformMatrix3fv(this.uDisplay, false, this.display);
  201. gl.uniform1f(this.uCurrentTime, performance.now() / 1000.0);
  202. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
  203. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
  204. gl.enableVertexAttribArray(this.aPosition);
  205. gl.enableVertexAttribArray(this.aOffset);
  206. gl.vertexAttribPointer(this.aPosition, 2, gl.FLOAT, false, 16, 0);
  207. gl.vertexAttribPointer(this.aOffset, 2, gl.FLOAT, false, 16, 8);
  208. gl.enable(gl.BLEND);
  209. gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
  210. gl.drawElements(
  211. gl.TRIANGLES,
  212. this.indexBufferSize,
  213. gl.UNSIGNED_SHORT,
  214. 0
  215. );
  216. // Request new render because markers are animated.
  217. this.requestRender();
  218. },
  219. // Called by the map view or the popup view when hit testing is required.
  220. hitTest: function(x, y) {
  221. // The map view.
  222. var view = this.view;
  223. if (this.layer.graphics.length === 0) {
  224. // Nothing to do.
  225. return promiseUtils.resolve(null);
  226. }
  227. // Compute screen distance between each graphic and the test point.
  228. var distances = this.layer.graphics.map(function(graphic) {
  229. var graphicPoint = view.toScreen(graphic.geometry);
  230. return Math.sqrt(
  231. (graphicPoint.x - x) * (graphicPoint.x - x) +
  232. (graphicPoint.y - y) * (graphicPoint.y - y)
  233. );
  234. });
  235. // Find the minimum distance.
  236. var minIndex = 0;
  237. distances.forEach(function(distance, i) {
  238. if (distance < distances.getItemAt(minIndex)) {
  239. minIndex = i;
  240. }
  241. });
  242. var minDistance = distances.getItemAt(minIndex);
  243. // If the minimum distance is more than 35 pixel then nothing was hit.
  244. if (minDistance > 35) {
  245. return promiseUtils.resolve(null);
  246. }
  247. // Otherwise it is a hit; We set the layer as the source layer for the graphic
  248. // (required for the popup view to work) and we return a resolving promise to
  249. // the graphic.
  250. var graphic = this.layer.graphics.getItemAt(minIndex);
  251. graphic.sourceLayer = this.layer;
  252. return promiseUtils.resolve(graphic);
  253. },
  254. // Called internally from render().
  255. updatePositions: function(renderParameters) {
  256. var gl = renderParameters.context;
  257. var stationary = renderParameters.stationary;
  258. var state = renderParameters.state;
  259. // If we are not stationary we simply update the `translationToCenter` vector.
  260. if (!stationary) {
  261. vec2.sub(
  262. this.translationToCenter,
  263. this.centerAtLastUpdate,
  264. state.center
  265. );
  266. this.requestRender();
  267. return;
  268. }
  269. // If we are stationary, the `layer.graphics` collection has not changed, and
  270. // we are centered on the `centerAtLastUpdate`, we do nothing.
  271. if (
  272. !this.needsUpdate &&
  273. this.translationToCenter[0] === 0 &&
  274. this.translationToCenter[1] === 0
  275. ) {
  276. return;
  277. }
  278. // Otherwise, we record the new encoded center, which imply a reset of the `translationToCenter` vector,
  279. // we record the update time, and we proceed to update the buffers.
  280. this.centerAtLastUpdate.set(state.center);
  281. this.translationToCenter[0] = 0;
  282. this.translationToCenter[1] = 0;
  283. this.needsUpdate = false;
  284. var graphics = this.layer.graphics;
  285. // Generate vertex data.
  286. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
  287. var vertexData = new Float32Array(16 * graphics.length);
  288. var i = 0;
  289. graphics.forEach(
  290. function(graphic) {
  291. var point = graphic.geometry;
  292. // The (x, y) position is relative to the encoded center.
  293. var x = point.x - this.centerAtLastUpdate[0];
  294. var y = point.y - this.centerAtLastUpdate[1];
  295. vertexData[i * 16 + 0] = x;
  296. vertexData[i * 16 + 1] = y;
  297. vertexData[i * 16 + 2] = -0.5;
  298. vertexData[i * 16 + 3] = -0.5;
  299. vertexData[i * 16 + 4] = x;
  300. vertexData[i * 16 + 5] = y;
  301. vertexData[i * 16 + 6] = 0.5;
  302. vertexData[i * 16 + 7] = -0.5;
  303. vertexData[i * 16 + 8] = x;
  304. vertexData[i * 16 + 9] = y;
  305. vertexData[i * 16 + 10] = -0.5;
  306. vertexData[i * 16 + 11] = 0.5;
  307. vertexData[i * 16 + 12] = x;
  308. vertexData[i * 16 + 13] = y;
  309. vertexData[i * 16 + 14] = 0.5;
  310. vertexData[i * 16 + 15] = 0.5;
  311. ++i;
  312. }.bind(this)
  313. );
  314. gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW);
  315. // Generates index data.
  316. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
  317. var indexData = new Uint16Array(6 * graphics.length);
  318. for (var i = 0; i < graphics.length; ++i) {
  319. indexData[i * 6 + 0] = i * 4 + 0;
  320. indexData[i * 6 + 1] = i * 4 + 1;
  321. indexData[i * 6 + 2] = i * 4 + 2;
  322. indexData[i * 6 + 3] = i * 4 + 1;
  323. indexData[i * 6 + 4] = i * 4 + 3;
  324. indexData[i * 6 + 5] = i * 4 + 2;
  325. }
  326. gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW);
  327. // Record number of indices.
  328. this.indexBufferSize = indexData.length;
  329. }
  330. });
  331. // Subclass the custom layer view from GraphicsLayer.
  332. var CustomLayer = GraphicsLayer.createSubclass({
  333. createLayerView: function(view) {
  334. // We only support MapView, so we only need to return a
  335. // custom layer view for the `2d` case.
  336. if (view.type === "2d") {
  337. return new CustomLayerView2D({
  338. view: view,
  339. layer: this
  340. });
  341. }
  342. }
  343. });
  344. // Create an instance of the custom layer with 4 initial graphics.
  345. corrugatedPointlayer = new CustomLayer({
  346. id:"corrugatedLayer",
  347. popupTemplate: popupTemplate,
  348. graphics: graphicsList
  349. });
  350. m_map.add(corrugatedPointlayer)
  351. });
  352. }