/** * Created by xdw on 2020/5/9. */ //需要gl-matrix.js /* * popupTemplate={ title: "{NAME}", content: "Population: {value}." } graphicsList=[ { geometry: { type: "point", x: -80, y: 35 }, attributes: { NAME: "Los Angeles", value: 3792621 } } ] * * * */ var corrugatedPointlayer; function createCorrugatedPoint (popupTemplate,graphicsList,map) { require([ "esri/core/watchUtils", "esri/core/promiseUtils", "esri/geometry/support/webMercatorUtils", "esri/layers/GraphicsLayer", "esri/views/2d/layers/BaseLayerViewGL2D" ], function( watchUtils, promiseUtils, webMercatorUtils, GraphicsLayer, BaseLayerViewGL2D ) { m_map.remove(corrugatedPointlayer); // Subclass the custom layer view from BaseLayerViewGL2D. var CustomLayerView2D = BaseLayerViewGL2D.createSubclass({ // Locations of the two vertex attributes that we use. They will be bound to the shader program before linking. aPosition: 0, aOffset: 1, constructor: function() { // Geometrical transformations that must be recomputed from scratch at every frame. this.transform = mat3.create(); this.translationToCenter = vec2.create(); this.screenTranslation = vec2.create(); // Geometrical transformations whose only a few elements must be updated per frame. Those elements are marked with NaN. this.display = mat3.fromValues(NaN, 0, 0, 0, NaN, 0, -1, 1, 1); this.screenScaling = vec3.fromValues(NaN, NaN, 1); // Whether the vertex and index buffers need to be updated due to a change in the layer data. this.needsUpdate = false; // We listen for changes to the graphics collection of the layer // and trigger the generation of new frames. A frame rendered while // `needsUpdate` is true may cause an update of the vertex and // index buffers. var requestUpdate = function() { this.needsUpdate = true; this.requestRender(); }.bind(this); this.watcher = watchUtils.on( this, "layer.graphics", "change", requestUpdate, requestUpdate, requestUpdate ); }, // Called once a custom layer is added to the map.layers collection and this layer view is instantiated. attach: function() { var gl = this.context; // Define and compile shaders. var vertexSource = "precision highp float;" + "uniform mat3 u_transform;" + "uniform mat3 u_display;" + "attribute vec2 a_position;" + "attribute vec2 a_offset;" + "varying vec2 v_offset;" + "const float SIZE = 100.0;" +//直径 "void main() {" + " gl_Position.xy = (u_display * (u_transform * vec3(a_position, 1.0) + vec3(a_offset * SIZE, 0.0))).xy;" + " gl_Position.zw = vec2(0.0, 1.0);" + " v_offset = a_offset;" + "}"; var fragmentSource = "precision highp float;" + "uniform float u_current_time;" + "varying vec2 v_offset;" + "const float PI = 3.14159;" + "const float N_RINGS = 3.0;" + "const vec3 COLOR = vec3(0.23, 0.43, 0.70);" +//颜色修改 "const float FREQ = 1.0;" +//频率 "void main() {" + " float l = length(v_offset);" + " 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);" + " gl_FragColor = vec4(COLOR * intensity, intensity);" + "}"; var vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertexSource); gl.compileShader(vertexShader); var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragmentSource); gl.compileShader(fragmentShader); // Create the shader program. this.program = gl.createProgram(); gl.attachShader(this.program, vertexShader); gl.attachShader(this.program, fragmentShader); // Bind attributes. gl.bindAttribLocation(this.program, this.aPosition, "a_position"); gl.bindAttribLocation(this.program, this.aOffset, "a_offset"); // Link. gl.linkProgram(this.program); // Shader objects are not needed anymore. gl.deleteShader(vertexShader); gl.deleteShader(fragmentShader); // Retrieve uniform locations once and for all. this.uTransform = gl.getUniformLocation( this.program, "u_transform" ); this.uDisplay = gl.getUniformLocation(this.program, "u_display"); this.uCurrentTime = gl.getUniformLocation( this.program, "u_current_time" ); // Create the vertex and index buffer. They are initially empty. We need to track the // size of the index buffer because we use indexed drawing. this.vertexBuffer = gl.createBuffer(); this.indexBuffer = gl.createBuffer(); // Number of indices in the index buffer. this.indexBufferSize = 0; // When certain conditions occur, we update the buffers and re-compute and re-encode // all the attributes. When buffer update occurs, we also take note of the current center // of the view state, and we reset a vector called `translationToCenter` to [0, 0], meaning that the // current center is the same as it was when the attributes were recomputed. this.centerAtLastUpdate = vec2.fromValues( this.view.state.center[0], this.view.state.center[1] ); }, // Called once a custom layer is removed from the map.layers collection and this layer view is destroyed. detach: function() { // Stop watching the `layer.graphics` collection. this.watcher.remove(); var gl = this.context; // Delete buffers and programs. gl.deleteBuffer(this.vertexBuffer); gl.deleteBuffer(this.indexBuffer); gl.deleteProgram(this.program); }, // Called every time a frame is rendered. render: function(renderParameters) { var gl = renderParameters.context; var state = renderParameters.state; // Update vertex positions. This may trigger an update of // the vertex coordinates contained in the vertex buffer. // There are three kinds of updates: // - Modification of the layer.graphics collection ==> Buffer update // - The view state becomes non-stationary ==> Only view update, no buffer update // - The view state becomes stationary ==> Buffer update this.updatePositions(renderParameters); // If there is nothing to render we return. if (this.indexBufferSize === 0) { return; } // Update view `transform` matrix; it converts from map units to pixels. mat3.identity(this.transform); this.screenTranslation[0] = (state.pixelRatio * state.size[0]) / 2; this.screenTranslation[1] = (state.pixelRatio * state.size[1]) / 2; mat3.translate( this.transform, this.transform, this.screenTranslation ); mat3.rotate( this.transform, this.transform, (Math.PI * state.rotation) / 180 ); this.screenScaling[0] = state.pixelRatio / state.resolution; this.screenScaling[1] = -state.pixelRatio / state.resolution; mat3.scale(this.transform, this.transform, this.screenScaling); mat3.translate( this.transform, this.transform, this.translationToCenter ); // Update view `display` matrix; it converts from pixels to normalized device coordinates. this.display[0] = 2 / (state.pixelRatio * state.size[0]); this.display[4] = -2 / (state.pixelRatio * state.size[1]); // Draw. gl.useProgram(this.program); gl.uniformMatrix3fv(this.uTransform, false, this.transform); gl.uniformMatrix3fv(this.uDisplay, false, this.display); gl.uniform1f(this.uCurrentTime, performance.now() / 1000.0); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.enableVertexAttribArray(this.aPosition); gl.enableVertexAttribArray(this.aOffset); gl.vertexAttribPointer(this.aPosition, 2, gl.FLOAT, false, 16, 0); gl.vertexAttribPointer(this.aOffset, 2, gl.FLOAT, false, 16, 8); gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); gl.drawElements( gl.TRIANGLES, this.indexBufferSize, gl.UNSIGNED_SHORT, 0 ); // Request new render because markers are animated. this.requestRender(); }, // Called by the map view or the popup view when hit testing is required. hitTest: function(x, y) { // The map view. var view = this.view; if (this.layer.graphics.length === 0) { // Nothing to do. return promiseUtils.resolve(null); } // Compute screen distance between each graphic and the test point. var distances = this.layer.graphics.map(function(graphic) { var graphicPoint = view.toScreen(graphic.geometry); return Math.sqrt( (graphicPoint.x - x) * (graphicPoint.x - x) + (graphicPoint.y - y) * (graphicPoint.y - y) ); }); // Find the minimum distance. var minIndex = 0; distances.forEach(function(distance, i) { if (distance < distances.getItemAt(minIndex)) { minIndex = i; } }); var minDistance = distances.getItemAt(minIndex); // If the minimum distance is more than 35 pixel then nothing was hit. if (minDistance > 35) { return promiseUtils.resolve(null); } // Otherwise it is a hit; We set the layer as the source layer for the graphic // (required for the popup view to work) and we return a resolving promise to // the graphic. var graphic = this.layer.graphics.getItemAt(minIndex); graphic.sourceLayer = this.layer; return promiseUtils.resolve(graphic); }, // Called internally from render(). updatePositions: function(renderParameters) { var gl = renderParameters.context; var stationary = renderParameters.stationary; var state = renderParameters.state; // If we are not stationary we simply update the `translationToCenter` vector. if (!stationary) { vec2.sub( this.translationToCenter, this.centerAtLastUpdate, state.center ); this.requestRender(); return; } // If we are stationary, the `layer.graphics` collection has not changed, and // we are centered on the `centerAtLastUpdate`, we do nothing. if ( !this.needsUpdate && this.translationToCenter[0] === 0 && this.translationToCenter[1] === 0 ) { return; } // Otherwise, we record the new encoded center, which imply a reset of the `translationToCenter` vector, // we record the update time, and we proceed to update the buffers. this.centerAtLastUpdate.set(state.center); this.translationToCenter[0] = 0; this.translationToCenter[1] = 0; this.needsUpdate = false; var graphics = this.layer.graphics; // Generate vertex data. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); var vertexData = new Float32Array(16 * graphics.length); var i = 0; graphics.forEach( function(graphic) { var point = graphic.geometry; // The (x, y) position is relative to the encoded center. var x = point.x - this.centerAtLastUpdate[0]; var y = point.y - this.centerAtLastUpdate[1]; vertexData[i * 16 + 0] = x; vertexData[i * 16 + 1] = y; vertexData[i * 16 + 2] = -0.5; vertexData[i * 16 + 3] = -0.5; vertexData[i * 16 + 4] = x; vertexData[i * 16 + 5] = y; vertexData[i * 16 + 6] = 0.5; vertexData[i * 16 + 7] = -0.5; vertexData[i * 16 + 8] = x; vertexData[i * 16 + 9] = y; vertexData[i * 16 + 10] = -0.5; vertexData[i * 16 + 11] = 0.5; vertexData[i * 16 + 12] = x; vertexData[i * 16 + 13] = y; vertexData[i * 16 + 14] = 0.5; vertexData[i * 16 + 15] = 0.5; ++i; }.bind(this) ); gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW); // Generates index data. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); var indexData = new Uint16Array(6 * graphics.length); for (var i = 0; i < graphics.length; ++i) { indexData[i * 6 + 0] = i * 4 + 0; indexData[i * 6 + 1] = i * 4 + 1; indexData[i * 6 + 2] = i * 4 + 2; indexData[i * 6 + 3] = i * 4 + 1; indexData[i * 6 + 4] = i * 4 + 3; indexData[i * 6 + 5] = i * 4 + 2; } gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW); // Record number of indices. this.indexBufferSize = indexData.length; } }); // Subclass the custom layer view from GraphicsLayer. var CustomLayer = GraphicsLayer.createSubclass({ createLayerView: function(view) { // We only support MapView, so we only need to return a // custom layer view for the `2d` case. if (view.type === "2d") { return new CustomLayerView2D({ view: view, layer: this }); } } }); // Create an instance of the custom layer with 4 initial graphics. corrugatedPointlayer = new CustomLayer({ id:"corrugatedLayer", popupTemplate: popupTemplate, graphics: graphicsList }); m_map.add(corrugatedPointlayer) }); }