{"id":1322972,"date":"2021-08-31T17:30:08","date_gmt":"2021-09-01T00:30:08","guid":{"rendered":"https:\/\/www.esri.com\/arcgis-blog\/?post_type=blog&#038;p=1322972"},"modified":"2024-04-12T03:59:02","modified_gmt":"2024-04-12T10:59:02","slug":"visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer","status":"publish","type":"blog","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer","title":{"rendered":"Visualize and animate flow in MapView with a custom WebGL layer"},"author":7921,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"open","ping_status":"closed","template":"","format":"standard","meta":{"_acf_changed":false,"_searchwp_excluded":""},"categories":[738191],"tags":[],"industry":[],"product":[761642,36831],"class_list":["post-1322972","blog","type-blog","status-publish","format-standard","hentry","category-developers","product-platform","product-js-api-arcgis"],"acf":{"short_description":"Learn how to animate streamlines using WebGL and a custom layer.","flexible_content":[{"acf_fc_layout":"content","content":"<p><strong>Introduction<\/strong><\/p>\n<p>This article discusses <em>visualizing wind and water currents through animated streamlines<\/em>; the <em>out-of-the-box<\/em> capabilities of the ArcGIS API for JavaScript (ArcGIS JS API) are combined with custom WebGL code to create compelling animated visualizations of real-world atmospheric and marine data.<\/p>\n<p><a href=\"https:\/\/wind-es.s3.us-west-1.amazonaws.com\/demos\/winds.html\">See it live<\/a> or check out the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\">source code on GitHub<\/a>.<\/p>\n<p>Custom layers are an advanced topic; familiarity with WebGL and custom WebGL layer views is recommended. A good place to get started with extending the ArcGIS JS API with custom WebGL layer views is the <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/custom-gl-visuals\/\">official SDK sample<\/a>. And remember that your fellow developers at <a href=\"https:\/\/community.esri.com\">community.esri.com<\/a> are always happy to help!<\/p>\n<p>With that said&#8230; <em>everyone deserves amazing maps<\/em>! Streamlines are a natural drawing style for flow datasets, and our team is considering adding them to the ArcGIS JS API. Join <a href=\"https:\/\/community.esri.com\/t5\/arcgis-api-for-javascript-blog\/streamlines-and-flow-animation-in-the-arcgis-api\/ba-p\/1094235\">the discussion on community.esri.com<\/a> and share your ideas on how to bring this awesomeness to a larger audience!<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1333622,"id":1333622,"title":"animate-flow","filename":"animate-flow-1.gif","filesize":4593475,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/animate-flow-1.gif","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/animate-flow-2","alt":"","author":"7921","description":"","caption":"","name":"animate-flow-2","status":"inherit","uploaded_to":1322972,"date":"2021-08-23 19:37:16","modified":"2021-08-23 19:37:16","menu_order":0,"mime_type":"image\/gif","type":"image","subtype":"gif","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":762,"height":446,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/animate-flow-1-213x200.gif","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/animate-flow-1.gif","medium-width":446,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/animate-flow-1.gif","medium_large-width":762,"medium_large-height":446,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/animate-flow-1.gif","large-width":762,"large-height":446,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/animate-flow-1.gif","1536x1536-width":762,"1536x1536-height":446,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/animate-flow-1.gif","2048x2048-width":762,"2048x2048-height":446,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/animate-flow-1.gif","card_image-width":762,"card_image-height":446,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/animate-flow-1.gif","wide_image-width":762,"wide_image-height":446}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/wind-es.s3.us-west-1.amazonaws.com\/demos\/winds.html"},{"acf_fc_layout":"content","content":"<p><strong>The power of animations<\/strong><\/p>\n<p>Awesome datasets need awesome visualizations. The ArcGIS JS API ships with <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/intro-mapview\/\">2D<\/a> and <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/intro-sceneview\/\">3D<\/a> support and a variety of <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-layers-Layer.html\">layer types<\/a>, <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-renderers-Renderer.html\">renderers<\/a>, <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/intro-effect-layer\/\">effects<\/a>, and <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/blendmode-darkening\/\">blend modes<\/a> that should cover the requirements of most users most of the time.<\/p>\n<p>In this blog post, we focus on <em>animated visualizations<\/em>; animations can capture and communicate the dynamicity of certain datasets more effectively than static graphics. The ArcGIS JS API supports several forms of animations <em>out-of-the-box<\/em> and my colleague Anne Fitz penned down <a href=\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/animating-your-data-with-the-arcgis-api-for-javascript\/\">a great writeup<\/a> covering several useful techniques that are applicable in a broad range of situations. With that said, certain applications call for a more customized experience; this is where <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/custom-gl-visuals\/\">custom WebGL layers<\/a> come into play.<\/p>\n<p>The images below show the same area of the map rendered in three different ways. For this article, we are focusing on an<a href=\"https:\/\/jsapi.maps.arcgis.com\/home\/item.html?id=e1d83802495d4e4eb29405555608d1a0\"> imagery tile layer\u00a0containing wind magnitude and direction for the continental United States<\/a>.<\/p>\n<ul>\n<li>Predefined <code>ImageryTileLayer<\/code> with <code>\"raster-stretch\"<\/code> renderer (left). This option does a good job at visualizing wind speed, but the direction information is lost.<\/li>\n<li>Predefined <code>ImageryTileLayer<\/code> with <code>\"vector-field\"<\/code> renderer (center). Using arrow symbols and size and rotation visual variables, this renderer can visualize both aspects of the wind. <code>ImageryTileLayer<\/code> support for this renderer is shipping with version 4.21 of the ArcGIS JS API; before that, this new functionality is available in the <a href=\"https:\/\/github.com\/Esri\/feedback-js-api-next#cdn\">next<\/a> build.<\/li>\n<li>Custom WebGL layer displaying animated flow lines, as described in this article (right). Our custom visualization provides a more intuitive representation of wind currents; the different animation speed of the lines maps to the underlying concept of speed magnitude, and the continuous nature of the visualization makes it easier to spot patterns in the data, like that rotational flow near the Canadian border. Also, it looks pretty cool.<\/li>\n<\/ul>\n"},{"acf_fc_layout":"image","image":{"ID":1340202,"id":1340202,"title":"comparison","filename":"comparison.png","filesize":48349,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/comparison.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/comparison-3","alt":"","author":"7921","description":"","caption":"","name":"comparison-3","status":"inherit","uploaded_to":1322972,"date":"2021-08-31 20:38:46","modified":"2021-08-31 20:38:46","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1059,"height":228,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/comparison-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/comparison.png","medium-width":464,"medium-height":100,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/comparison.png","medium_large-width":768,"medium_large-height":165,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/comparison.png","large-width":1059,"large-height":228,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/comparison.png","1536x1536-width":1059,"1536x1536-height":228,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/comparison.png","2048x2048-width":1059,"2048x2048-height":228,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/comparison-826x178.png","card_image-width":826,"card_image-height":178,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/comparison.png","wide_image-width":1059,"wide_image-height":228}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/comparison.png"},{"acf_fc_layout":"content","content":"<p>This article describes in depth the implementation of a custom WebGL layer that displays <em>animated streamlines<\/em>. A streamline is the path that a massless particle would take when immersed in the fluid. Please note that the focus of the article is on the flow visualization algorithm and integration with the ArcGIS JS API; whether the particular technique is suitable for a given dataset, audience, or application, needs to be evaluated by a domain expert, and the implementation modified as needed.<\/p>\n"},{"acf_fc_layout":"content","content":"<p><strong>Load, transform and render<\/strong><\/p>\n<p>As anything that shows up on a computer screen, GIS visualizations are the result of:<\/p>\n<ol>\n<li>Loading the data;<\/li>\n<li>Optionally, transforming\/processing\/preparing the data;<\/li>\n<li>Rendering the data!<\/li>\n<\/ol>\n"},{"acf_fc_layout":"image","image":{"ID":1326492,"id":1326492,"title":"load-transform-render","filename":"load-transform-render.png","filesize":16053,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/load-transform-render.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/load-transform-render","alt":"","author":"7921","description":"","caption":"","name":"load-transform-render","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 08:24:33","modified":"2021-08-17 08:24:33","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1077,"height":141,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/load-transform-render-213x141.png","thumbnail-width":213,"thumbnail-height":141,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/load-transform-render.png","medium-width":464,"medium-height":61,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/load-transform-render.png","medium_large-width":768,"medium_large-height":101,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/load-transform-render.png","large-width":1077,"large-height":141,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/load-transform-render.png","1536x1536-width":1077,"1536x1536-height":141,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/load-transform-render.png","2048x2048-width":1077,"2048x2048-height":141,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/load-transform-render-826x108.png","card_image-width":826,"card_image-height":108,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/load-transform-render.png","wide_image-width":1077,"wide_image-height":141}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p>Each of the predefined layer types that ship with the ArcGIS JS API is a software component that bundles together two capabilities.<\/p>\n<ul>\n<li>The ability to access data, either from local memory or from a remote source;<\/li>\n<li>The ability to render the retrieved data, often both in 2D <code>MapView<\/code> and 3D <code>SceneView<\/code>.<\/li>\n<\/ul>\n<p>In the ArcGIS JS API, the implementation of a layer type consists of a <em>layer<\/em> class and one or two <em>layer view<\/em> classes. With relation to the three phases discussed above, the layer is mostly responsible for accessing the data (1) while 2D layer views and 3D layer views take care of the rendering (3). Data transformation (2) can be required for different reasons and, to some degree, it is carried out both by layers and layer views.<\/p>\n<p><strong>Polylines with timestamps<\/strong><\/p>\n<p>At the core of a visualization such as the one that we are setting out to build, there is the concept of a <em>m-aware polyline feature<\/em>. This is a polyline where each vertex, in addition to its coordinates, carries one or more <em>m-values<\/em>. An m-value can be thought of as a position dependent attribute of the polyline, that varies smoothly along its path; for vertices the m-value is given explicitly, while for any points between two vertices it can be obtained by linearly interpolating the value at the vertices.<\/p>\n<p>A common application of m-aware polylines is representing <em>paths<\/em> or, as in this article, <em>streamlines<\/em>; in this case the m-values are the timestamps at which that vertex is visited by the particle.<\/p>\n<p>If your data is already stored in an m-aware polyline feature layer, <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-layers-FeatureLayer.html#queryFeatures\"><code>FeatureLayer.queryFeatures()<\/code><\/a> can be used to retrieve the polylines and access the m-value information.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1326462,"id":1326462,"title":"m-polyline","filename":"m-polyline.png","filesize":14408,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/m-polyline.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/m-polyline","alt":"","author":"7921","description":"","caption":"","name":"m-polyline","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 08:21:06","modified":"2021-08-17 08:21:06","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":878,"height":319,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/m-polyline-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/m-polyline.png","medium-width":464,"medium-height":169,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/m-polyline.png","medium_large-width":768,"medium_large-height":279,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/m-polyline.png","large-width":878,"large-height":319,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/m-polyline.png","1536x1536-width":878,"1536x1536-height":319,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/m-polyline.png","2048x2048-width":878,"2048x2048-height":319,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/m-polyline-826x300.png","card_image-width":826,"card_image-height":300,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/m-polyline.png","wide_image-width":878,"wide_image-height":319}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p><strong>Catching wind<\/strong><\/p>\n<p>In this article we will not ingest polyline features; we will build the polylines starting from flow data contained in an <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-layers-ImageryTileLayer.html\"><code>ImageryTileLayer<\/code><\/a>. This layer type is similar to <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-layers-ImageryLayer.html\"><code>ImageryLayer<\/code><\/a> in the sense that it contains raster LERC2D data, but the way that the data is stored on the server is different; imagery tile layers store data into static files called <em>tiles<\/em>, that are cloud and cache-friendly. Each tile contains many pixels, and each pixel contains one or many <em>bands<\/em> of data. In the case of an imagery tile layer that stores wind data, there are two bands that together describe wind direction and speed.<\/p>\n<p>As it is the case with any other predefined layer, an <code>ImageryTileLayer<\/code> can be added to a map and displayed by a <code>MapView<\/code>. The simplest way to visualize an imagery tile layer is with a <code>\"raster-stretch\"<\/code> renderer. The default settings are usually sensible, and the resulting visuals are easy to interpret with the help of a legend. See <a href=\"https:\/\/codepen.io\/dawken\/pen\/eYWXXjN?editors=1000\">the <code>\"raster-stretch\"<\/code> renderer in CodePen<\/a>.<\/p>\n<p>We will focus our attention on imagery tile layers that store flow information, such as wind and marine currents. For these kind of layers, the <code>\"vector-field\"<\/code> renderer is supported and used as the default renderer. See <a href=\"https:\/\/codepen.io\/dawken\/pen\/NWjeNRz\">the <code>\"raster-stretch\"<\/code> renderer in CodePen<\/a>.<\/p>\n<p><strong>Watch this space!<\/strong><\/p>\n<p>The input to a rendering algorithm is visual in nature, or at the very least can be interpreted as visual. For instance, <em>positions<\/em> are expressed as numbers and the rendering algorithm is responsible for drawing geographic entities in the right places according to those numbers. However, numbers by themselves are meaningless, and they only get their positional semantics from being associated to particular <em>spaces<\/em>.<\/p>\n<p>In the course of this article we are going to refer to three spaces; each space plays its own role in the implementation of the streamlines visualization.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1326472,"id":1326472,"title":"spaces","filename":"spaces.png","filesize":38733,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/spaces.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/spaces","alt":"","author":"7921","description":"","caption":"","name":"spaces","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 08:22:25","modified":"2021-08-17 08:22:25","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":988,"height":342,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/spaces-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/spaces.png","medium-width":464,"medium-height":161,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/spaces.png","medium_large-width":768,"medium_large-height":266,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/spaces.png","large-width":988,"large-height":342,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/spaces.png","1536x1536-width":988,"1536x1536-height":342,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/spaces.png","2048x2048-width":988,"2048x2048-height":342,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/spaces-826x286.png","card_image-width":826,"card_image-height":286,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/spaces.png","wide_image-width":988,"wide_image-height":342}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p><em>Map space<\/em><\/p>\n<p>Map space is used to locate the data on the server. In the case of the wind imagery tile layer that we are considering in this article, it is given by the EPSG:4326 reference system. Map space is right-handed and its coordinates are expressed in <em>map units<\/em>.<\/p>\n<p><em>Screen space<\/em><\/p>\n<p>Screen space is the left handed frame where <code>+X<\/code> is right, <code>+Y<\/code> is down, the origin is in the upper-left corner of the MapView, while the lower-right corner has for coordinates the width and height of the MapView. Screen space coordinates are expressed in <em>pixels<\/em>.<\/p>\n<p><em>Particle space (aka model space, aka object space aka local space)<\/em><\/p>\n<p>The animated lines are the result of a <em>particle simulation<\/em>. The position of a particle as it is swept by the simulated wind traces the polyline that will be later animated. The position of a particle is expressed in particle space. The units of the particle space are called <em>cells<\/em> and are related to pixels by a multiplicative constant, called <code>cellSize<\/code>. In the figure above we assume <code>cellSize<\/code> of 10 so that a <code>1920x1080<\/code> pixels <code>MapView<\/code> results in a <code>192x108<\/code> particle space.<\/p>\n<p>We will see that streamlines ultimately will be rendered using triangle meshes; the coordinates of mesh vertices will be expressed in particle space; in computer graphic, this space is often called <a href=\"https:\/\/developer.download.nvidia.com\/CgTutorial\/cg_tutorial_chapter04.html\"><em>model space<\/em>, <em>object space<\/em><\/a>, or sometimes even <em>local space<\/em>.<\/p>\n<p><strong>Vector fields<\/strong><\/p>\n<p>A field is a position-dependent property of a space. Temperature, pressure, humidity are all examples of <em>scalar<\/em> fields common in meteorology. In other words, a scalar field maps a point to a numeric value. In GIS systems scalar fields are often represented using one band of an imagery tile layer.<\/p>\n<p>Some properties are multidimensional in nature, and they are called <em>vector<\/em> fields. This article focuses on a specific type of vector field, useful to represent wind data, called a <em>velocity field<\/em>; a velocity field associates a velocity vector to each point of a space. A velocity is a <em>\u201cdirectional speed\u201d<\/em> and can be represented using two bands of an imagery tile layer. There are two different ways to encode 2D velocities.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1326482,"id":1326482,"title":"vector-field","filename":"vector-field.png","filesize":31181,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vector-field.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/vector-field","alt":"","author":"7921","description":"","caption":"","name":"vector-field","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 08:23:41","modified":"2021-08-17 08:23:41","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":373,"height":489,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vector-field-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vector-field.png","medium-width":199,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vector-field.png","medium_large-width":373,"medium_large-height":489,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vector-field.png","large-width":373,"large-height":489,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vector-field.png","1536x1536-width":373,"1536x1536-height":489,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vector-field.png","2048x2048-width":373,"2048x2048-height":489,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vector-field-355x465.png","card_image-width":355,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vector-field.png","wide_image-width":373,"wide_image-height":489}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p><em>Magnitude and direction<\/em><\/p>\n<p>This is reported as <code>\u201cdataType\u201d: \u201cesriImageServiceDataTypeVector-MagDir\u201d<\/code> in the raster info section of the layer.<\/p>\n<p>The direction of the wind is measured in degrees, with <code>0<\/code> being North and <code>90<\/code> being East. The magnitude is a speed.<\/p>\n<p><em>UV<\/em><\/p>\n<p>This is reported as <code>\u201cdataType\u201d: \u201cesriImageServiceDataTypeVector-UV\u201d<\/code> in the raster info section of the layer.<\/p>\n<p><code>U<\/code> is a the west-east component of the speed and <code>V<\/code> is the south-north component.<\/p>\n"},{"acf_fc_layout":"content","content":"<p><strong>Fetching the LERC2D wind data<\/strong><\/p>\n<p>In the ArcGIS JS API, layers do not have to be added to a map to be useful; several layer types offer data fetching capabilities.<\/p>\n<p>This is especially remarkable for <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-layers-ImageryTileLayer.html#fetchPixels\"><code>ImageryTileLayer.fetchPixels()<\/code><\/a>, which enables querying arbitrary extents in spite of the fact that the queried data is broken into tiles on the CDN. The implementation of <code>ImageryTileLayer<\/code> takes care of downloading the (possibly cached) tiles and stitches them together in a single image that spans the required extent.<\/p>\n<p>An instance of <code>ImageryTileLayer<\/code> can be used to fetch the data that a custom WebGL layer view needs. Every time that the <code>MapView<\/code> becomes stationary, the custom WebGL layer view triggers a refresh of the visualization. The current extent of the visualization is passed to <code>ImageryTileLayer.fetchPixels()<\/code> together with the size of the desired output LERC2D image. Instead of querying an image of the same size of the <code>MapView<\/code>, we ask for a downsampled image by a factor of <code>cellSize<\/code>, which is fixed at <code>5<\/code> in the current implementation. As an example, if the <code>MapView<\/code> was 1280&#215;720, the code would fetch a 256&#215;144 image from the image server. We do this to save bandwidth, reduce processing time, and \u201cregularize\u201d the data; full-resolution wind data may have high-frequency components that are likely to destabilize the simulator. The downsampled image represents the wind in particle space and each data element is called a <em>cell<\/em>.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1326512,"id":1326512,"title":"fetch-pixels","filename":"fetch-pixels.png","filesize":30953,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fetch-pixels.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/fetch-pixels","alt":"","author":"7921","description":"","caption":"","name":"fetch-pixels","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 08:45:41","modified":"2021-08-17 08:45:41","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1212,"height":716,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fetch-pixels-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fetch-pixels.png","medium-width":442,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fetch-pixels.png","medium_large-width":768,"medium_large-height":454,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fetch-pixels.png","large-width":1212,"large-height":716,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fetch-pixels.png","1536x1536-width":1212,"1536x1536-height":716,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fetch-pixels.png","2048x2048-width":1212,"2048x2048-height":716,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fetch-pixels-787x465.png","card_image-width":787,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fetch-pixels.png","wide_image-width":1212,"wide_image-height":716}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fetch-pixels.png"},{"acf_fc_layout":"content","content":"<p>The <code>rawCellData<\/code> stores the two bands of the wind separately, in two distinct, row-major, equally sized matrices. Whether these two bands are MagDir or UV can be determined by examining the <code>rasterInfo.dataType<\/code> property. We chose to normalize the values to <em>particle space UV<\/em>, which is more convenient for the simulation, and interleave the two bands into a single matrix. Note that the original values would have probably been expressed in knots or meters per second, but from now on we will assume that all velocities are in particle space.<\/p>\n<p>There are a couple of things worth mentioning about the conversion formulas.<\/p>\n<p>In the <em>&#8220;MagDir to UV case&#8221;<\/em> the magnitude is multiplied by sine and cosine factors to get the U and V components respectively. Note how the direction is first converted to radians, and then it is delayed by <code>Math.PI \/ 2<\/code>; this is needed because in particle space positive angles rotate the +X axis over the +Y axis, while in map space positive angles rotate the +Y axis (the <em>&#8220;North&#8221;<\/em>) over the +X axis (the <em>&#8220;East&#8221;<\/em>). These two conventions are both clockwise but angles in map space are a quarter of a full circle <em>late<\/em>.<\/p>\n<p>In the <em>&#8220;UV to UV case&#8221;<\/em>, the U value can be copied but the V value needs to be flipped, because in particle space <code>+Y<\/code> goes down while in map space it goes up.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1326522,"id":1326522,"title":"to-uv","filename":"to-uv.png","filesize":44698,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/to-uv.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/to-uv","alt":"","author":"7921","description":"","caption":"","name":"to-uv","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 08:47:05","modified":"2021-08-17 08:47:05","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1114,"height":1148,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/to-uv-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/to-uv.png","medium-width":253,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/to-uv.png","medium_large-width":768,"medium_large-height":791,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/to-uv.png","large-width":1048,"large-height":1080,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/to-uv.png","1536x1536-width":1114,"1536x1536-height":1148,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/to-uv.png","2048x2048-width":1114,"2048x2048-height":1148,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/to-uv-451x465.png","card_image-width":451,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/to-uv-1048x1080.png","wide_image-width":1048,"wide_image-height":1080}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/to-uv.png"},{"acf_fc_layout":"content","content":"<p>The output of the fragment of code above is the <code>data<\/code> variable that essentially is a discretized velocity field in particle space; <em>i.e.<\/em>, each cell in particle space is mapped to a horizontal velocity (<code>+X<\/code> is right) and a vertical velocity (<code>+Y<\/code> is down), also in particle space.<\/p>\n<p>In the rest of the article, <code>FlowData<\/code> denotes a type that holds the <code>data<\/code> variable, together with the grid dimensions and the <code>cellSize<\/code> factor. <code>FlowData<\/code> is the input to the particle simulation code that traces the streamlines in particle space.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1327352,"id":1327352,"title":"flow-data","filename":"flow-data.png","filesize":9893,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/flow-data.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/flow-data","alt":"","author":"7921","description":"","caption":"","name":"flow-data","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 16:18:43","modified":"2021-08-17 16:18:43","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":726,"height":536,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/flow-data-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/flow-data.png","medium-width":354,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/flow-data.png","medium_large-width":726,"medium_large-height":536,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/flow-data.png","large-width":726,"large-height":536,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/flow-data.png","1536x1536-width":726,"1536x1536-height":536,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/flow-data.png","2048x2048-width":726,"2048x2048-height":536,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/flow-data-630x465.png","card_image-width":630,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/flow-data.png","wide_image-width":726,"wide_image-height":536}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/flow-data.png"},{"acf_fc_layout":"content","content":"<p><strong>Pro tip: smooth the data<\/strong><\/p>\n<p>We already <em>\u201csmoothed\u201d<\/em> the flow data by requesting the image server a lower resolution image than the <code>MapView<\/code> itself. In addition to this, for certain datasets an explicit smoothing using a separable <a href=\"https:\/\/en.wikipedia.org\/wiki\/Gaussian_filter\">Gaussian kernel<\/a> can help obtaining more robust and aesthetically pleasing results.<\/p>\n"},{"acf_fc_layout":"content","content":"<p><strong>Turning the particle space vector velocity field into a function<\/strong><\/p>\n<p>For ease of use by the particle simulator, we wrap the <code>flowData.data<\/code> typed array into a closure; the closure takes a point <code>(x, y)<\/code> in particle space and returns a velocity again in particle space.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1326532,"id":1326532,"title":"field-closure","filename":"field-closure.png","filesize":29958,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/field-closure.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/field-closure","alt":"","author":"7921","description":"","caption":"","name":"field-closure","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 08:47:41","modified":"2021-08-17 08:47:41","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1696,"height":824,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/field-closure-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/field-closure.png","medium-width":464,"medium-height":225,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/field-closure.png","medium_large-width":768,"medium_large-height":373,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/field-closure.png","large-width":1696,"large-height":824,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/field-closure-1536x746.png","1536x1536-width":1536,"1536x1536-height":746,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/field-closure.png","2048x2048-width":1696,"2048x2048-height":824,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/field-closure-826x401.png","card_image-width":826,"card_image-height":401,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/field-closure.png","wide_image-width":1696,"wide_image-height":824}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/field-closure.png"},{"acf_fc_layout":"content","content":"<p><strong>Pro tip: adopt bilinear interpolation<\/strong><\/p>\n<p>A simulated particle will have more often than not a fractional position, i.e. <code>(42.23, 89.54)<\/code>. With such input, the field closure defined above would take the velocity in cell <code>(43, 90)<\/code> and return that. A better way of coming up with a value for fractional positions is to use <em>bilinear interpolation<\/em>; this allows for the velocity field to vary smoothly across a single cell and could be required to get acceptable results from the particle simulation when the wind data is low resolution.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1326552,"id":1326552,"title":"bilinear-interpolation","filename":"bilinear-interpolation.png","filesize":34253,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear-interpolation.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/bilinear-interpolation","alt":"","author":"7921","description":"","caption":"","name":"bilinear-interpolation","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 08:59:41","modified":"2021-08-17 08:59:41","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":889,"height":358,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear-interpolation-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear-interpolation.png","medium-width":464,"medium-height":187,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear-interpolation.png","medium_large-width":768,"medium_large-height":309,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear-interpolation.png","large-width":889,"large-height":358,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear-interpolation.png","1536x1536-width":889,"1536x1536-height":358,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear-interpolation.png","2048x2048-width":889,"2048x2048-height":358,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear-interpolation-826x333.png","card_image-width":826,"card_image-height":333,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear-interpolation.png","wide_image-width":889,"wide_image-height":358}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p>Bilinear interpolation works by selecting the four closest cells; first two adjacent pairs of cells are interpolated vertically, based on the distance of the desired point from the cells&#8217; centers, and then the results of the first interpolation are interpolated again horizontally. See <a href=\"https:\/\/en.wikipedia.org\/wiki\/Bilinear_interpolation\">bilinear interpolation<\/a> for more details.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1326542,"id":1326542,"title":"bilinear","filename":"bilinear.png","filesize":41605,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/bilinear","alt":"","author":"7921","description":"","caption":"","name":"bilinear","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 08:51:40","modified":"2021-08-17 08:51:40","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1004,"height":1112,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear.png","medium-width":236,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear.png","medium_large-width":768,"medium_large-height":851,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear.png","large-width":975,"large-height":1080,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear.png","1536x1536-width":1004,"1536x1536-height":1112,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear.png","2048x2048-width":1004,"2048x2048-height":1112,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear-420x465.png","card_image-width":420,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear-975x1080.png","wide_image-width":975,"wide_image-height":1080}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bilinear.png"},{"acf_fc_layout":"content","content":"<p><strong>Particle simulation<\/strong><\/p>\n<p>Particles are immersed in the closure-wrapped <code>FlowData<\/code> field at a pseudo-random (but repeatable, using a seeded generator) positions and simulated using the <em>trace()<\/em> function.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1327362,"id":1327362,"title":"simulation","filename":"simulation.png","filesize":19102,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/simulation.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/simulation","alt":"","author":"7921","description":"","caption":"","name":"simulation","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 16:29:58","modified":"2021-08-17 16:29:58","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1628,"height":572,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/simulation-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/simulation.png","medium-width":464,"medium-height":163,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/simulation.png","medium_large-width":768,"medium_large-height":270,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/simulation.png","large-width":1628,"large-height":572,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/simulation-1536x540.png","1536x1536-width":1536,"1536x1536-height":540,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/simulation.png","2048x2048-width":1628,"2048x2048-height":572,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/simulation-826x290.png","card_image-width":826,"card_image-height":290,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/simulation.png","wide_image-width":1628,"wide_image-height":572}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/simulation.png"},{"acf_fc_layout":"content","content":"<p>Simulating the movement of a particle in a velocity field <code>vf<\/code> is an iterative process, for which there are a couple of different approaches. The simplest one uses a <em>fixed time step<\/em> and increments the position of the particle by an amount proportional to the velocity at that point and the time step. Each iteration produces a new vertex of the polyline and, together with the previous vertex, a new segment.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1329002,"id":1329002,"title":"sim-1","filename":"sim-1.png","filesize":12388,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-1.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/sim-1","alt":"","author":"7921","description":"","caption":"","name":"sim-1","status":"inherit","uploaded_to":1322972,"date":"2021-08-18 20:10:19","modified":"2021-08-18 20:10:19","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":535,"height":205,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-1-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-1.png","medium-width":464,"medium-height":178,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-1.png","medium_large-width":535,"medium_large-height":205,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-1.png","large-width":535,"large-height":205,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-1.png","1536x1536-width":535,"1536x1536-height":205,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-1.png","2048x2048-width":535,"2048x2048-height":205,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-1.png","card_image-width":535,"card_image-height":205,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-1.png","wide_image-width":535,"wide_image-height":205}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p>Fixing the time step is a perfectly sound approach but for the demo app we opted for a <em>fixed segment length<\/em> instead. This approach causes line lengths to vary less and also guarantees that adjacent vertices are not too close to each other; we think that both these properties are desirable for this kind of visualization, but the reader is encouraged to experiment with different iteration strategies.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1328982,"id":1328982,"title":"sim-2","filename":"sim-2.png","filesize":42742,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-2.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/sim-2","alt":"","author":"7921","description":"","caption":"","name":"sim-2","status":"inherit","uploaded_to":1322972,"date":"2021-08-18 20:07:16","modified":"2021-08-18 20:07:16","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":864,"height":490,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-2-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-2.png","medium-width":460,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-2.png","medium_large-width":768,"medium_large-height":436,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-2.png","large-width":864,"large-height":490,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-2.png","1536x1536-width":864,"1536x1536-height":490,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-2.png","2048x2048-width":864,"2048x2048-height":490,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-2-820x465.png","card_image-width":820,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/sim-2.png","wide_image-width":864,"wide_image-height":490}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p>In the demo app, each particle is update <code>30<\/code> times using a <code>for<\/code> loop and the segment length is <code>1<\/code> cell; together with a <code>cellSize<\/code> of <code>5<\/code> pixels, this leads to each individual polyline having a length of about <code>150<\/code> pixels. At each iteration the speed of the particle is tested and, if found to be too low (because the particle entered a cell where the wind is weak), the simulation for that particle is terminated.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1327372,"id":1327372,"title":"trace","filename":"trace.png","filesize":61451,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/trace.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/trace-3","alt":"","author":"7921","description":"","caption":"","name":"trace-3","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 16:30:29","modified":"2021-08-17 16:30:29","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1558,"height":1580,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/trace-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/trace.png","medium-width":257,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/trace.png","medium_large-width":768,"medium_large-height":779,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/trace.png","large-width":1065,"large-height":1080,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/trace-1515x1536.png","1536x1536-width":1515,"1536x1536-height":1536,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/trace.png","2048x2048-width":1558,"2048x2048-height":1580,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/trace-459x465.png","card_image-width":459,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/trace-1065x1080.png","wide_image-width":1065,"wide_image-height":1080}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/trace.png"},{"acf_fc_layout":"content","content":"<p><strong>Building the triangle mesh<\/strong><\/p>\n<p>WebGL has poor built-in support for line drawing; all the line rendering capabilities that ship the ArcGIS JS API are based on triangle meshes, and for this custom flow WebGL we are taking a similar approach. For more information, check out the <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/custom-gl-animated-lines\/\">SDK sample about animated lines<\/a>. Also, the reader should be aware that the ArcGIS JS API ships with <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/custom-gl-tessellation-helpers\/\">tessellation helper methods<\/a> that can convert arbitrary geometries into triangle meshes.<\/p>\n<p>For representing streamlines our visual requirements are quite basic and we are going to stick with a very simple method; each segment of a polyline is represented by a thin rectangle, at most 2-3 pixels wide, lying along the original polyline; the original polyline becomes the centerline of this <em>&#8220;rectangle chain&#8221;<\/em>. Each rectangle is made of <code>4<\/code> vertices, <code>6<\/code> indices, and renders as a pair of right <code>gl.TRIANGLES<\/code> sharing their hypothenuse. This approach leads to gaps and regions of overlaps, but it is a very fast and robust algorithm, and for thin lines, the artifacts are not noticeable.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1329242,"id":1329242,"title":"dirty-trick","filename":"dirty-trick.png","filesize":11660,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/dirty-trick.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/dirty-trick","alt":"","author":"7921","description":"","caption":"","name":"dirty-trick","status":"inherit","uploaded_to":1322972,"date":"2021-08-18 22:11:33","modified":"2021-08-18 22:11:33","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":749,"height":233,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/dirty-trick-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/dirty-trick.png","medium-width":464,"medium-height":144,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/dirty-trick.png","medium_large-width":749,"medium_large-height":233,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/dirty-trick.png","large-width":749,"large-height":233,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/dirty-trick.png","1536x1536-width":749,"1536x1536-height":233,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/dirty-trick.png","2048x2048-width":749,"2048x2048-height":233,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/dirty-trick.png","card_image-width":749,"card_image-height":233,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/dirty-trick.png","wide_image-width":749,"wide_image-height":233}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p>In the next paragraphs, we call <em>polyline vertex<\/em> a vertex of the original polyline, as produced by the particle simulation algorithm. Such vertex is <em>extruded<\/em> in directions perpendicular to the centerline to produce <em>mesh vertices<\/em>; these are the vertices that are written to the WebGL vertex buffer.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1329262,"id":1329262,"title":"extrusion-vectors","filename":"extrusion-vectors.png","filesize":46396,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/extrusion-vectors.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/extrusion-vectors","alt":"","author":"7921","description":"","caption":"","name":"extrusion-vectors","status":"inherit","uploaded_to":1322972,"date":"2021-08-18 22:12:28","modified":"2021-08-18 22:12:28","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":817,"height":278,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/extrusion-vectors-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/extrusion-vectors.png","medium-width":464,"medium-height":158,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/extrusion-vectors.png","medium_large-width":768,"medium_large-height":261,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/extrusion-vectors.png","large-width":817,"large-height":278,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/extrusion-vectors.png","1536x1536-width":817,"1536x1536-height":278,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/extrusion-vectors.png","2048x2048-width":817,"2048x2048-height":278,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/extrusion-vectors.png","card_image-width":817,"card_image-height":278,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/extrusion-vectors.png","wide_image-width":817,"wide_image-height":278}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p>The most powerful feature of low-level, GPU-accelerated graphic APIs like WebGL, that really sets them apart from Canvas2D, GDI+ and all the other higher-level solutions, is the ability to <em>define custom visual properties<\/em>, and use them in <em>custom programs called shaders<\/em>. This enables applications to describe complex scenes, with many different, possibly animated, objects, and render them with little assistance from the CPU. This approach greatly reduces the load on the CPU, and the GPU is free to run at full speed.<\/p>\n<p>There are going to be about a thousand streamlines in a typical flow visualization; we want to <em>render all of them using a single draw call<\/em>. To achieve this, we need to store all triangle meshes for all polylines in the same vertex and index buffers. The code snippet below does just that. Note that since this could be a long-running process, the algorithm is designed to transfer control back to the event loop once in a while so that the UI thread does not freeze (lines <code>12-15<\/code>).<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1329632,"id":1329632,"title":"create-mesh - Copy","filename":"create-mesh-Copy.png","filesize":100494,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/create-mesh-Copy.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/create-mesh-copy","alt":"","author":"7921","description":"","caption":"","name":"create-mesh-copy","status":"inherit","uploaded_to":1322972,"date":"2021-08-19 06:55:40","modified":"2021-08-19 06:55:40","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1640,"height":2264,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/create-mesh-Copy-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/create-mesh-Copy.png","medium-width":189,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/create-mesh-Copy.png","medium_large-width":768,"medium_large-height":1060,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/create-mesh-Copy.png","large-width":782,"large-height":1080,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/create-mesh-Copy-1113x1536.png","1536x1536-width":1113,"1536x1536-height":1536,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/create-mesh-Copy-1484x2048.png","2048x2048-width":1484,"2048x2048-height":2048,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/create-mesh-Copy-337x465.png","card_image-width":337,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/create-mesh-Copy-782x1080.png","wide_image-width":782,"wide_image-height":1080}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/create-mesh-Copy.png"},{"acf_fc_layout":"content","content":"<p>The custom properties that we need to define fall into 4 categories.<\/p>\n<p><em>Per-frame<\/em><br \/>\nThese properties are shared by all polylines and are updated every frame. They are passed to the shaders using <em>uniform<\/em> values. Since they do not need to be stored in the vertex buffer, we will talk about them when discussing rendering.<\/p>\n<p><em>Per-feature<\/em><br \/>\nThese properties are associated with the polyline itself. The only way to implement this kind of property in WebGL 1, while at the same time maintaining the ability to draw all polylines with a single draw call, is to actually make them vertex attributes, and repeat the same values for all vertices of the same feature.<\/p>\n<p>There are 2 per-feature properties:<\/p>\n<p>&#8211; <code>totalTime<\/code> is the total runtime in seconds for the polyline. The fragment shader needs this value at every fragment in order to properly loop the animation.<br \/>\n&#8211; <code>random<\/code> is a pseudo-random value, again needed in the fragment shader to introduce some temporal differences in the animation, so that streamlines with the same length do not end up synchronized.<\/p>\n<p>They are highlighted in orange in the code snippet above.<\/p>\n<p><em>Per-polyline-vertex<\/em><br \/>\nThese properties are associated with the polyline vertex itself. There are 2 per-polyline-vertex properties:<\/p>\n<p>&#8211; <code>x, y<\/code>, the vertex position, in particle space units, <em>i.e.<\/em>, <em>cells<\/em>.<br \/>\n&#8211; <code>t<\/code>, the vertex timestamp.<\/p>\n<p>They are marked in green in the code snippet.<\/p>\n<p><em>Per-mesh-vertex<\/em><\/p>\n<p>Each polyline vertex is extruded into two <code>mesh vertices<\/code>, one per side of the polyline. There are 2 per-mesh-vertex properties.<\/p>\n<p>&#8211; <code>ex, ey<\/code> is the <code>extrusion vector<\/code>. This is computed at lines <code>32-34<\/code> by normalizing the segment vector and rotating it 90 degrees. Being normalized, its magnitude is meaningless but you can imagine it being expressed in particle space units, <em>i.e.<\/em>, <em>cells<\/em>.<br \/>\n&#8211; a <code>+1\/-1 constant<\/code> that we call <em>side<\/em>, and identifies an extruded vertex as lying on the right side of the centerline, or on the left side.<\/p>\n<p>They are marked in blue in the code snippet.<\/p>\n<p>For more information about constructing and rendering line meshes, see the <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/custom-gl-animated-lines\/\">SDK sample about animated lines<\/a>.<\/p>\n"},{"acf_fc_layout":"content","content":"<p><strong>Rendering<\/strong><\/p>\n<p>Now is time to put the GPU to work and render our beautiful streamlines!<\/p>\n<p>Before discussing rendering there is one additional space that needs to be introduced; it is called <em>clip space<\/em> and just like screen space describes positions on the screen, but using a different coordinate system. In clip space, the origin is in the center and the drawing area is seen as a <code>2x2<\/code> rectangle, where <code>+X<\/code> is right and <code>+Y<\/code> is up. This space is the space of the <code>gl_Position<\/code> variable.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1327312,"id":1327312,"title":"clip-space","filename":"clip-space.png","filesize":4135,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/clip-space.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/clip-space","alt":"","author":"7921","description":"","caption":"","name":"clip-space","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 16:06:57","modified":"2021-08-17 16:06:57","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":322,"height":252,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/clip-space-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/clip-space.png","medium-width":322,"medium-height":252,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/clip-space.png","medium_large-width":322,"medium_large-height":252,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/clip-space.png","large-width":322,"large-height":252,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/clip-space.png","1536x1536-width":322,"1536x1536-height":252,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/clip-space.png","2048x2048-width":322,"2048x2048-height":252,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/clip-space.png","card_image-width":322,"card_image-height":252,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/clip-space.png","wide_image-width":322,"wide_image-height":252}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p>The rendering process takes the particle space mesh and draws it to the screen. As the user pans and zooms, the visualization needs to be repositioned to reflect the change in view point, until a new mesh is ready that again can be rendered full screen.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1326572,"id":1326572,"title":"particle-to-screen","filename":"particle-to-screen.png","filesize":77022,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/particle-to-screen","alt":"","author":"7921","description":"","caption":"","name":"particle-to-screen","status":"inherit","uploaded_to":1322972,"date":"2021-08-17 09:03:19","modified":"2021-08-17 09:03:19","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1003,"height":351,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen.png","medium-width":464,"medium-height":162,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen.png","medium_large-width":768,"medium_large-height":269,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen.png","large-width":1003,"large-height":351,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen.png","1536x1536-width":1003,"1536x1536-height":351,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen.png","2048x2048-width":1003,"2048x2048-height":351,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen-826x289.png","card_image-width":826,"card_image-height":289,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen.png","wide_image-width":1003,"wide_image-height":351}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"image","image":{"ID":1329282,"id":1329282,"title":"particle-to-screen-with-ev","filename":"particle-to-screen-with-ev.png","filesize":54062,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen-with-ev.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/particle-to-screen-with-ev","alt":"","author":"7921","description":"","caption":"","name":"particle-to-screen-with-ev","status":"inherit","uploaded_to":1322972,"date":"2021-08-18 22:14:19","modified":"2021-08-18 22:14:19","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":492,"height":516,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen-with-ev-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen-with-ev.png","medium-width":249,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen-with-ev.png","medium_large-width":492,"medium_large-height":516,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen-with-ev.png","large-width":492,"large-height":516,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen-with-ev.png","1536x1536-width":492,"1536x1536-height":516,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen-with-ev.png","2048x2048-width":492,"2048x2048-height":516,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen-with-ev-443x465.png","card_image-width":443,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/particle-to-screen-with-ev.png","wide_image-width":492,"wide_image-height":516}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p><strong>Vertex shader<\/strong><\/p>\n<p>The vertex shader converts the coordinates of mesh vertices from particle space to screen space using the <code>u_ScreenFromLocal<\/code> matrix, extrudes them according to the extrusion vector, and then transforms everything to clip space using the <code>u_ClipFromScreen<\/code> matrix. Note how the extrusion vectors are rotated by the <code>u_Rotation<\/code> matrix and scaled by half line width but are not subject to the same transformation that particle space coordinates go through, which also includes a scale factor when zooming in and out; this separation between position and extrusion vectors is responsible for the anti-zoom behavior of lines, which always exhibit the same width. The value <code>u_PixelRatio<\/code> is used to display lines always of the desired width, even when the DPI of the screen is very high or very low.<\/p>\n<p>All other attributes are passed verbatim to the fragment shader using <code>varying<\/code> variables.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1330712,"id":1330712,"title":"vs","filename":"vs-1.png","filesize":61438,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vs-1.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/vs-2","alt":"","author":"7921","description":"","caption":"","name":"vs-2","status":"inherit","uploaded_to":1322972,"date":"2021-08-19 19:35:40","modified":"2021-08-19 19:35:40","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":2048,"height":1328,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vs-1-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vs-1.png","medium-width":403,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vs-1.png","medium_large-width":768,"medium_large-height":498,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vs-1.png","large-width":1666,"large-height":1080,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vs-1-1536x996.png","1536x1536-width":1536,"1536x1536-height":996,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vs-1.png","2048x2048-width":2048,"2048x2048-height":1328,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vs-1-717x465.png","card_image-width":717,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vs-1-1666x1080.png","wide_image-width":1666,"wide_image-height":1080}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vs-1.png"},{"acf_fc_layout":"content","content":"<p><strong>Fragment shader<\/strong><\/p>\n<p>The fragment shader creates and animates the trail effect.<\/p>\n<p>It does so by taking advantage that each fragment is associated with a timestamp, computed automatically by the GPU interpolator based on the timestamps at the two closest vertices.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1329302,"id":1329302,"title":"zoom-on-segment","filename":"zoom-on-segment.png","filesize":43879,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/zoom-on-segment.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/zoom-on-segment","alt":"","author":"7921","description":"","caption":"","name":"zoom-on-segment","status":"inherit","uploaded_to":1322972,"date":"2021-08-18 22:14:26","modified":"2021-08-18 22:14:26","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":817,"height":655,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/zoom-on-segment-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/zoom-on-segment.png","medium-width":326,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/zoom-on-segment.png","medium_large-width":768,"medium_large-height":616,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/zoom-on-segment.png","large-width":817,"large-height":655,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/zoom-on-segment.png","1536x1536-width":817,"1536x1536-height":655,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/zoom-on-segment.png","2048x2048-width":817,"2048x2048-height":655,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/zoom-on-segment-580x465.png","card_image-width":580,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/zoom-on-segment.png","wide_image-width":817,"wide_image-height":655}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p>The next snippet shows the fragment shader source.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1330732,"id":1330732,"title":"fs","filename":"fs-1.png","filesize":49375,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fs-1.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/fs-2","alt":"","author":"7921","description":"","caption":"","name":"fs-2","status":"inherit","uploaded_to":1322972,"date":"2021-08-19 19:36:11","modified":"2021-08-19 19:36:11","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1820,"height":1256,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fs-1-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fs-1.png","medium-width":378,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fs-1.png","medium_large-width":768,"medium_large-height":530,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fs-1.png","large-width":1565,"large-height":1080,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fs-1-1536x1060.png","1536x1536-width":1536,"1536x1536-height":1060,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fs-1.png","2048x2048-width":1820,"2048x2048-height":1256,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fs-1-674x465.png","card_image-width":674,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fs-1-1565x1080.png","wide_image-width":1565,"wide_image-height":1080}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fs-1.png"},{"acf_fc_layout":"content","content":"<p>The base color of the streamlines is taken from uniform <code>u_Color<\/code>. The fragment shader modifies the opacity of the fragments to implement the animated trail effect.<\/p>\n<p>A fragment tends to be opaque when its timestamp is close but not greater than the current time, which is passed to the shader as the uniform <code>u_Time<\/code>; this is done at lines <code>16-23<\/code> using an exponential function applied to a periodized time-dependent signal.<\/p>\n<p>A fragment is also more opaque on the centerline than near the edges of the rectangle; this effect is applied at line <code>14<\/code> by taking advantage of the fact that the <code>a_Side<\/code> attribute has an absolute value of <code>1<\/code> near the edges, and <code>0<\/code> on the centerline.<\/p>\n<p>Finally, at line <code>25<\/code> the output color is premultiplied because <code>MapView<\/code> will not composite the layer correctly otherwise.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1329272,"id":1329272,"title":"fragment-shading","filename":"fragment-shading.png","filesize":21393,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fragment-shading.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/fragment-shading","alt":"","author":"7921","description":"","caption":"","name":"fragment-shading","status":"inherit","uploaded_to":1322972,"date":"2021-08-18 22:13:01","modified":"2021-08-18 22:13:01","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1029,"height":163,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fragment-shading-213x163.png","thumbnail-width":213,"thumbnail-height":163,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fragment-shading.png","medium-width":464,"medium-height":74,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fragment-shading.png","medium_large-width":768,"medium_large-height":122,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fragment-shading.png","large-width":1029,"large-height":163,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fragment-shading.png","1536x1536-width":1029,"1536x1536-height":163,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fragment-shading.png","2048x2048-width":1029,"2048x2048-height":163,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fragment-shading-826x131.png","card_image-width":826,"card_image-height":131,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fragment-shading.png","wide_image-width":1029,"wide_image-height":163}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/fragment-shading.png"},{"acf_fc_layout":"content","content":"<p><strong>Configuring WebGL and issuing the draw call<\/strong><\/p>\n<p>We are ready for rendering! The rendering algorithm consists of about 80 lines of WebGL state setting and a single draw call.<\/p>\n<p>&#8211; Bind the mesh (lines <code>7-23<\/code>)<br \/>\n&#8211; Build the <code>u_ClipFromScreen<\/code> matrix, that transforms from screen space to clip space (lines <code>27-33<\/code>).<br \/>\n&#8211; Build the <code>u_ScreenFromLocal<\/code> matrix, that transforms from particle space to screen space (lines <code>38-49<\/code>).<br \/>\n&#8211; Build the <code>u_Rotation<\/code> matrix, used to rotate extrusion vectors (lines <code>35-36<\/code>).<br \/>\n&#8211; Configure the shader program (lines <code>51-76<\/code>).<br \/>\n&#8211; Enable premultiplied alpha (lines <code>78-79<\/code>).<br \/>\n&#8211; Draw all the streamlines at once (line <code>81<\/code>).<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1330762,"id":1330762,"title":"render-visualization","filename":"render-visualization-2.png","filesize":242941,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/render-visualization-2.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/render-visualization-3","alt":"","author":"7921","description":"","caption":"","name":"render-visualization-3","status":"inherit","uploaded_to":1322972,"date":"2021-08-19 19:39:08","modified":"2021-08-19 19:39:08","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1834,"height":3272,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/render-visualization-2-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/render-visualization-2.png","medium-width":146,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/render-visualization-2.png","medium_large-width":768,"medium_large-height":1370,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/render-visualization-2.png","large-width":605,"large-height":1080,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/render-visualization-2-861x1536.png","1536x1536-width":861,"1536x1536-height":1536,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/render-visualization-2-1148x2048.png","2048x2048-width":1148,"2048x2048-height":2048,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/render-visualization-2-261x465.png","card_image-width":261,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/render-visualization-2-605x1080.png","wide_image-width":605,"wide_image-height":1080}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/render-visualization-2.png"},{"acf_fc_layout":"content","content":"<p><strong>Putting everything together<\/strong><\/p>\n<p>In the source repository the code is chiefly organized around two packages; <code>core<\/code> and <code>flow<\/code>.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1330422,"id":1330422,"title":"packages","filename":"packages.png","filesize":15157,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/packages.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/packages","alt":"","author":"7921","description":"","caption":"","name":"packages","status":"inherit","uploaded_to":1322972,"date":"2021-08-19 16:47:08","modified":"2021-08-19 16:47:08","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":620,"height":647,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/packages-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/packages.png","medium-width":250,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/packages.png","medium_large-width":620,"medium_large-height":647,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/packages.png","large-width":620,"large-height":647,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/packages.png","1536x1536-width":620,"1536x1536-height":647,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/packages.png","2048x2048-width":620,"2048x2048-height":647,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/packages-446x465.png","card_image-width":446,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/packages.png","wide_image-width":620,"wide_image-height":647}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p><strong>core<\/strong><br \/>\nThe <code>core<\/code> package contains generic classes and utilities that are useful to create custom visualizations; it provides a simpler abstraction on top of <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html\"><code>BaseLayerViewGL2D<\/code><\/a> and implements the resource loading\/unloading lifecycle. It contains the following modules.<\/p>\n<p>&#8211; <code>types<\/code>. Type definitions and interfaces used by the entire codebase. Most importantly, it introduces the concept of <em>global resources<\/em> and <em>local resources<\/em>. Global resources are loaded at startup and do not need to be updated when the extent changes; local resources are tied to a particular extent and needs to destroyed and reloaded as the user navigates the map.<br \/>\n&#8211; <code>settings<\/code>. Constants that define some of the built-in behaviors of the <code>flow<\/code> package; in a real-world app these should probably be configurable at runtime.<br \/>\n&#8211; <code>util<\/code>. Functions of general utility.<br \/>\n&#8211; <code>rendering<\/code>. Defines the abstract class <code>VisualizationStyle<\/code>, a class that defines how to load global resources, how to load local resources for a given extent, and how to render a visualization. It is an abstraction over the <code>attach()<\/code>\/<code>render()<\/code>\/<code>detach()<\/code> contract offered by <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html\"><code>BaseLayerViewGL2D<\/code><\/a> and its concrete implementations can be ran and tested without a running <code>MapView<\/code>. You can even create a static image out of a visualization style, for instance to be used as a thumbnail, by calling method <code>VisualizationStyle.createImage()<\/code>.<br \/>\n&#8211; <code>view<\/code>. Defines the abstract class <code>VisualizationLayerView2D<\/code>, a subclass of <code>BaseLayerViewGL2D<\/code>. It offers a simplified programming model and resource management scheme designed around the concept of <code>visualizations<\/code>, which are basically graphic objects that cover the current extent. To implement a new custom layer, inherit from <code>VisualizationLayerView2D<\/code> and override method <code>createVisualizationStyle()<\/code>. If the custom visualization is animated, set the <code>animate<\/code> flag to <code>true<\/code>.<\/p>\n<p><strong>flow<\/strong><br \/>\nThe <code>flow<\/code> package depends on <code>core<\/code> and implements the streamline rendering logic.<\/p>\n<p>With relation to the architecture of a geographic visualization app, the <code>flow<\/code> package provides an implementation for each of the 3 required steps.<\/p>\n<ol>\n<li>Loading the data;<\/li>\n<li>Transforming\/processing\/preparing the data;<\/li>\n<li>Rendering the data.<\/li>\n<\/ol>\n<p>The <code>flow<\/code> package contains the following modules.<\/p>\n<p>&#8211; <code>types<\/code>. Type definitions and interfaces used by the <code>flow<\/code> package.<br \/>\n&#8211; <code>settings<\/code>. Constants that define some of the built-in behaviors of the <code>flow<\/code> package; in a real-world app these should probably be configurable at runtime.<br \/>\n&#8211; <code>sources<\/code>. Defines different strategies for loading (1) flow data; two strategies are supported at present time: <code>ImageryTileLayerFlowSource<\/code> that fetches LERC2D datasets from an imagery tile layer, and <code>VectorFieldFlowSource<\/code> that supports the analytic definition of a global velocity field in map units.<br \/>\n&#8211; <code>processors<\/code>. Defines the entire data transformation (2) pipeline, starting from flow data, particle simulation, conversion to streamlines and generation of the triangle mesh. Class <code>MainFlowProcessor<\/code> uses the main process, while <code>WorkerFlowProcessor<\/code> uses the <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-core-workers.html\">workers framework<\/a>.<br \/>\n&#8211; <code>shared<\/code>. Particle simulation and mesh generation code that can be invoked both by the main process, when <code>useWebWorkers<\/code> is <code>false<\/code>, and by the worker when it is <code>true<\/code>.<br \/>\n&#8211; <code>layer<\/code>. Defines the <code>FlowLayer<\/code> class by inheriting it from <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-layers-Layer.html\"><code>esri\/layers\/Layer<\/code><\/a>. This class overrides method <code>createLayerView()<\/code> to return an instance of <code>FlowLayerView2D<\/code>.<br \/>\n&#8211; <code>rendering<\/code>. Defines three classes: <code>FlowGlobalResources<\/code>, <code>FlowLocalResources<\/code> and <code>FlowVisualizationStyle<\/code>. These are concrete implementations of the abstract concepts defined in the same name module in the <code>core<\/code> package.<br \/>\n&#8211; <code>view<\/code>. Defines the <code>FlowLayerView2D<\/code> class by inheriting it from <code>VisualizationLayerView2D<\/code>. This class overrides method <code>createVisualizationStyle()<\/code> to return an instance of <code>FlowVisualizationStyle<\/code>.<\/p>\n"},{"acf_fc_layout":"content","content":"<p>The codebase contains a couple more interesting things that we have not been able to cover in this blog post, due to space constraints; first, <code>FlowLayer<\/code> defines a way to specify client-side flow data, which can be very useful for education and <em>what-if<\/em> scenarios where real data is not available.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1330562,"id":1330562,"title":"vortices","filename":"vortices.png","filesize":251067,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vortices.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/vortices","alt":"","author":"7921","description":"","caption":"","name":"vortices","status":"inherit","uploaded_to":1322972,"date":"2021-08-19 18:05:37","modified":"2021-08-19 18:05:37","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1141,"height":653,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vortices-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vortices.png","medium-width":456,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vortices.png","medium_large-width":768,"medium_large-height":440,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vortices.png","large-width":1141,"large-height":653,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vortices.png","1536x1536-width":1141,"1536x1536-height":653,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vortices.png","2048x2048-width":1141,"2048x2048-height":653,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vortices-813x465.png","card_image-width":813,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vortices.png","wide_image-width":1141,"wide_image-height":653}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/vortices.png"},{"acf_fc_layout":"content","content":"<p>Finally, <code>FlowLayer<\/code> supports running the particle simulation and mesh generation on <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-core-workers.html\">workers<\/a>, to reduce the load on the main thread that could lead to possible UI freezes. Workers are enabled by default and are controlled by the <code>useWebWorkers<\/code> flag.<\/p>\n"},{"acf_fc_layout":"content","content":"<p><strong>The main application file<\/strong><\/p>\n<p>The main application file declares a <code>VectorTileLayer<\/code> to be used as a basemap, an <code>ImageryTileLayer<\/code> that will be displayed by the standard vector field renderer, and our brand-new <code>FlowLayer<\/code> pointed to the same imagery tile layer URL.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1329432,"id":1329432,"title":"app","filename":"app.png","filesize":38935,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/app.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/app-3","alt":"","author":"7921","description":"","caption":"","name":"app-3","status":"inherit","uploaded_to":1322972,"date":"2021-08-18 22:21:44","modified":"2021-08-18 22:21:44","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1170,"height":968,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/app-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/app.png","medium-width":315,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/app.png","medium_large-width":768,"medium_large-height":635,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/app.png","large-width":1170,"large-height":968,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/app.png","1536x1536-width":1170,"1536x1536-height":968,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/app.png","2048x2048-width":1170,"2048x2048-height":968,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/app-562x465.png","card_image-width":562,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/app.png","wide_image-width":1170,"wide_image-height":968}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/app.png"},{"acf_fc_layout":"content","content":"<p>And&#8230; <em><strong>we are done<\/strong><\/em>!<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1330352,"id":1330352,"title":"huge","filename":"huge-1.png","filesize":925040,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/huge-1.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/huge-2","alt":"","author":"7921","description":"","caption":"","name":"huge-2","status":"inherit","uploaded_to":1322972,"date":"2021-08-19 15:56:29","modified":"2021-08-19 15:56:29","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":1920,"height":1080,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/huge-1-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/huge-1.png","medium-width":464,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/huge-1.png","medium_large-width":768,"medium_large-height":432,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/huge-1.png","large-width":1920,"large-height":1080,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/huge-1-1536x864.png","1536x1536-width":1536,"1536x1536-height":864,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/huge-1.png","2048x2048-width":1920,"2048x2048-height":1080,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/huge-1-826x465.png","card_image-width":826,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/huge-1.png","wide_image-width":1920,"wide_image-height":1080}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/wind-es.s3.us-west-1.amazonaws.com\/demos\/winds.html"},{"acf_fc_layout":"content","content":"<p>It is quite remarkable that a bunch of blue rectangles and less than 60 lines of shading code can look so pretty. The secret is that there is more shading going on behind the scenes; the <code>FlowLayer<\/code> that we just created is compatible with <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/blendmode-darkening\/\">blend modes<\/a> and <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/intro-effect-layer\/\">layer effects<\/a>. A large share of the visual appeal comes from specifying <code>effect: \"bloom(1.5, 0.5px, 0.2)\"<\/code> when creating the <code>FlowLayer<\/code> instance.<\/p>\n<p>The image below shows the positive influence of the <code>bloom<\/code> effect on our custom visualization. We encourage you to try other effects and blend modes, as well as stacking other predefined operational layers on top or below <code>FlowLayer<\/code>.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1329232,"id":1329232,"title":"bloom-comparison","filename":"bloom-comparison.png","filesize":73270,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bloom-comparison.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/bloom-comparison","alt":"","author":"7921","description":"","caption":"","name":"bloom-comparison","status":"inherit","uploaded_to":1322972,"date":"2021-08-18 22:11:06","modified":"2021-08-18 22:11:06","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/www.esri.com\/arcgis-blog\/wp-includes\/images\/media\/default.png","width":604,"height":233,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bloom-comparison-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bloom-comparison.png","medium-width":464,"medium-height":179,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bloom-comparison.png","medium_large-width":604,"medium_large-height":233,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bloom-comparison.png","large-width":604,"large-height":233,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bloom-comparison.png","1536x1536-width":604,"1536x1536-height":233,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bloom-comparison.png","2048x2048-width":604,"2048x2048-height":233,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bloom-comparison.png","card_image-width":604,"card_image-height":233,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bloom-comparison.png","wide_image-width":604,"wide_image-height":233}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/bloom-comparison.png"},{"acf_fc_layout":"content","content":"<p><strong>Conclusion<\/strong><\/p>\n<p>We hope you enjoyed this deep dive into flow visualization and animation using the ArcGIS JS API and custom WebGL layers. Check out the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\">source repository<\/a> and try to modify <code>FlowVisualizationStyle<\/code> to create your own dream layer.<\/p>\n<p>On behalf of the ArcGIS JS API team, we thank you for your interest in flow visualization; we are thinking that this drawing style is important enough that it should be a native capability of the ArcGIS JS API. We would love for you to join <a href=\"https:\/\/community.esri.com\/t5\/arcgis-api-for-javascript-blog\/streamlines-and-flow-animation-in-the-arcgis-api\/ba-p\/1094235\">the discussion on community.esri.com<\/a> and share your use case, workflow, or requirements with us.<\/p>\n<p>Happy coding!<\/p>\n"}],"authors":[{"ID":7921,"user_firstname":"Dario","user_lastname":"D'Amico","nickname":"DDamico","user_nicename":"ddamico","display_name":"Dario D'Amico","user_email":"DDamico@esri.com","user_url":"","user_registered":"2018-05-04 17:50:51","user_description":"Hello, my name is Dario! I majored in CS with a focus on formal verification methods at the University of Florence, Italy. I live in Redlands with my wife and two sons and work as a rendering engineer at Esri.","user_avatar":"<img data-del=\"avatar\" src='https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2018\/06\/32dd1ed.jpg' class='avatar pp-user-avatar avatar-96 photo ' height='96' width='96'\/>"}],"related_articles":[{"ID":1319622,"post_author":"10062","post_date":"2021-08-31 17:00:23","post_date_gmt":"2021-09-01 00:00:23","post_content":"","post_title":"Animating your data with the ArcGIS API for JavaScript","post_excerpt":"","post_status":"publish","comment_status":"open","ping_status":"closed","post_password":"","post_name":"animating-your-data-with-the-arcgis-api-for-javascript","to_ping":"","pinged":"","post_modified":"2024-04-12 03:57:29","post_modified_gmt":"2024-04-12 10:57:29","post_content_filtered":"","post_parent":0,"guid":"https:\/\/www.esri.com\/arcgis-blog\/?post_type=blog&#038;p=1319622","menu_order":0,"post_type":"blog","post_mime_type":"","comment_count":"0","filter":"raw"}],"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/826x465.png","wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/huge.png"},"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.9 (Yoast SEO v25.9) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Visualize and animate flow in MapView with a custom WebGL layer<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Visualize and animate flow in MapView with a custom WebGL layer\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\" \/>\n<meta property=\"og:site_name\" content=\"ArcGIS Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/esrigis\/\" \/>\n<meta property=\"article:modified_time\" content=\"2024-04-12T10:59:02+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:site\" content=\"@ESRI\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":[\"Article\",\"BlogPosting\"],\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\"},\"author\":{\"name\":\"Dario D'Amico\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/94907a22d3e4a02e7c0cb7a100917434\"},\"headline\":\"Visualize and animate flow in MapView with a custom WebGL layer\",\"datePublished\":\"2021-09-01T00:30:08+00:00\",\"dateModified\":\"2024-04-12T10:59:02+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\"},\"wordCount\":11,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#organization\"},\"articleSection\":[\"Developers\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\",\"url\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\",\"name\":\"Visualize and animate flow in MapView with a custom WebGL layer\",\"isPartOf\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#website\"},\"datePublished\":\"2021-09-01T00:30:08+00:00\",\"dateModified\":\"2024-04-12T10:59:02+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.esri.com\/arcgis-blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Visualize and animate flow in MapView with a custom WebGL layer\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#website\",\"url\":\"https:\/\/www.esri.com\/arcgis-blog\/\",\"name\":\"ArcGIS Blog\",\"description\":\"Get insider info from Esri product teams\",\"publisher\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.esri.com\/arcgis-blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#organization\",\"name\":\"Esri\",\"url\":\"https:\/\/www.esri.com\/arcgis-blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2018\/04\/Esri.png\",\"contentUrl\":\"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2018\/04\/Esri.png\",\"width\":400,\"height\":400,\"caption\":\"Esri\"},\"image\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/esrigis\/\",\"https:\/\/x.com\/ESRI\",\"https:\/\/www.linkedin.com\/company\/5311\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/94907a22d3e4a02e7c0cb7a100917434\",\"name\":\"Dario D'Amico\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2018\/06\/32dd1ed.jpg\",\"contentUrl\":\"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2018\/06\/32dd1ed.jpg\",\"caption\":\"Dario D'Amico\"},\"description\":\"Hello, my name is Dario! I majored in CS with a focus on formal verification methods at the University of Florence, Italy. I live in Redlands with my wife and two sons and work as a rendering engineer at Esri.\",\"url\":\"https:\/\/www.esri.com\/arcgis-blog\/author\/ddamico\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Visualize and animate flow in MapView with a custom WebGL layer","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer","og_locale":"en_US","og_type":"article","og_title":"Visualize and animate flow in MapView with a custom WebGL layer","og_url":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer","og_site_name":"ArcGIS Blog","article_publisher":"https:\/\/www.facebook.com\/esrigis\/","article_modified_time":"2024-04-12T10:59:02+00:00","twitter_card":"summary_large_image","twitter_site":"@ESRI","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":["Article","BlogPosting"],"@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer#article","isPartOf":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer"},"author":{"name":"Dario D'Amico","@id":"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/94907a22d3e4a02e7c0cb7a100917434"},"headline":"Visualize and animate flow in MapView with a custom WebGL layer","datePublished":"2021-09-01T00:30:08+00:00","dateModified":"2024-04-12T10:59:02+00:00","mainEntityOfPage":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer"},"wordCount":11,"commentCount":0,"publisher":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/#organization"},"articleSection":["Developers"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer","url":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer","name":"Visualize and animate flow in MapView with a custom WebGL layer","isPartOf":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/#website"},"datePublished":"2021-09-01T00:30:08+00:00","dateModified":"2024-04-12T10:59:02+00:00","breadcrumb":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.esri.com\/arcgis-blog\/"},{"@type":"ListItem","position":2,"name":"Visualize and animate flow in MapView with a custom WebGL layer"}]},{"@type":"WebSite","@id":"https:\/\/www.esri.com\/arcgis-blog\/#website","url":"https:\/\/www.esri.com\/arcgis-blog\/","name":"ArcGIS Blog","description":"Get insider info from Esri product teams","publisher":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.esri.com\/arcgis-blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.esri.com\/arcgis-blog\/#organization","name":"Esri","url":"https:\/\/www.esri.com\/arcgis-blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2018\/04\/Esri.png","contentUrl":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2018\/04\/Esri.png","width":400,"height":400,"caption":"Esri"},"image":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/esrigis\/","https:\/\/x.com\/ESRI","https:\/\/www.linkedin.com\/company\/5311\/"]},{"@type":"Person","@id":"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/94907a22d3e4a02e7c0cb7a100917434","name":"Dario D'Amico","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/image\/","url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2018\/06\/32dd1ed.jpg","contentUrl":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2018\/06\/32dd1ed.jpg","caption":"Dario D'Amico"},"description":"Hello, my name is Dario! I majored in CS with a focus on formal verification methods at the University of Florence, Italy. I live in Redlands with my wife and two sons and work as a rendering engineer at Esri.","url":"https:\/\/www.esri.com\/arcgis-blog\/author\/ddamico"}]}},"text_date":"August 31, 2021","author_name":"Dario D'Amico","author_page":"https:\/\/www.esri.com\/arcgis-blog\/author\/ddamico","custom_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2021\/08\/huge.png","primary_product":"ArcGIS Maps SDK for JavaScript","tag_data":[],"category_data":[{"term_id":738191,"name":"Developers","slug":"developers","term_group":0,"term_taxonomy_id":738191,"taxonomy":"category","description":"","parent":0,"count":426,"filter":"raw"}],"product_data":[{"term_id":761642,"name":"ArcGIS Location Platform","slug":"platform","term_group":0,"term_taxonomy_id":761642,"taxonomy":"product","description":"","parent":36601,"count":215,"filter":"raw"},{"term_id":36831,"name":"ArcGIS Maps SDK for JavaScript","slug":"js-api-arcgis","term_group":0,"term_taxonomy_id":36831,"taxonomy":"product","description":"","parent":36601,"count":363,"filter":"raw"}],"primary_product_link":"https:\/\/www.esri.com\/arcgis-blog\/?s=#&products=js-api-arcgis","_links":{"self":[{"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/blog\/1322972","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/blog"}],"about":[{"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/types\/blog"}],"author":[{"embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/users\/7921"}],"replies":[{"embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/comments?post=1322972"}],"version-history":[{"count":0,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/blog\/1322972\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/media?parent=1322972"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/categories?post=1322972"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/tags?post=1322972"},{"taxonomy":"industry","embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/industry?post=1322972"},{"taxonomy":"product","embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/product?post=1322972"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}