{"id":1502552,"date":"2022-03-31T10:45:31","date_gmt":"2022-03-31T17:45:31","guid":{"rendered":"https:\/\/www.esri.com\/arcgis-blog\/?post_type=blog&#038;p=1502552"},"modified":"2024-04-12T03:46:07","modified_gmt":"2024-04-12T10:46:07","slug":"jumpstart-your-custom-layer","status":"publish","type":"blog","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer","title":{"rendered":"Jumpstart your visualization: build on top of an existing custom 2D 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-1502552","blog","type-blog","status-publish","format-standard","hentry","category-developers","product-platform","product-js-api-arcgis"],"acf":{"short_description":"Learn how to take an existing custom 2D layer and use it as a starting point for your own unique visualization.","flexible_content":[{"acf_fc_layout":"content","content":"<p>Before we started shipping <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-renderers-FlowRenderer.html\">FlowRenderer<\/a> as part of the ArcGIS API for JavaScript (JS API), we published a <a href=\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer\/\">blog article<\/a> and an <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\">open-source TypeScript demo (repo animated-flow-ts on GitHub)<\/a> that implemented a simplified version of the same visualization as a custom WebGL layer. This effort was well received by our readers, who started experimenting with the code to adapt it to their use cases. Today, most common flow visualization use cases are handled by the official <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-renderers-FlowRenderer.html\">FlowRenderer<\/a>, but the original open-source repository <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\">animated-flow-ts<\/a> is still a valuable starting point if you need to satisfy a specific use case. In this article, we explain how to <strong>leverage the existing code and custom WebGL layers to create novel visualizations<\/strong>.<\/p>\n<p><strong>What we will build<\/strong><\/p>\n<p>The goal of this article is to explain how the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/apps\/jumpstart.ts\">jumpstart.ts<\/a> app (<a href=\"https:\/\/wind-es.s3.us-west-1.amazonaws.com\/demos\/jumpstart.html\"><b>live demo<\/b><\/a>) in the open-source repository works. This app packs 3 different custom WebGL layers and enables toggling between them using a <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-widgets-LayerList.html\">LayerList<\/a> widget.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1531982,"id":1531982,"title":"AllVIz","filename":"AllVIz.png","filesize":78937,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/AllVIz.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\/allviz","alt":"","author":"7921","description":"","caption":"","name":"allviz","status":"inherit","uploaded_to":1502552,"date":"2022-03-25 11:08:42","modified":"2022-03-25 11:08:42","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":953,"height":561,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/AllVIz-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/AllVIz.png","medium-width":443,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/AllVIz.png","medium_large-width":768,"medium_large-height":452,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/AllVIz.png","large-width":953,"large-height":561,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/AllVIz.png","1536x1536-width":953,"1536x1536-height":561,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/AllVIz.png","2048x2048-width":953,"2048x2048-height":561,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/AllVIz-790x465.png","card_image-width":790,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/AllVIz.png","wide_image-width":953,"wide_image-height":561}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/wind-es.s3.us-west-1.amazonaws.com\/demos\/jumpstart.html"},{"acf_fc_layout":"content","content":"<p>This article assumes familiarity with WebGL. Having experience with <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html\">BaseLayerViewGL2D<\/a> can be useful for gaining a deeper understanding of the codebase, but it is not a prerequisite; the code that we are going to write today does not import <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html\">BaseLayerViewGL2D<\/a> and is completely unaware of the fact that, behind the scenes, it is the entry point for all custom 2D WebGL rendering operations.<\/p>\n"},{"acf_fc_layout":"content","content":"<p><strong>A refresher on custom layers<\/strong><\/p>\n<p><em>This section briefly covers how to create custom layers from scratch; we included it for completeness and for providing context, but it is not required for understanding the rest of the article. <b>Feel free to skip to the next section if your focus today is on the rapid creation of new visualizations<\/b><\/em>.<\/p>\n<p>At Esri, we believe that you deserve the best tools for your projects. That is why the JS API ships with <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-layers-Layer.html\">many predefined layers<\/a>, ready to use and backed by our cloud offering.<\/p>\n<p>But we also know that you and your team may have unique requirements, calling for a custom integrated solution. For this reason, the 4x JS API has shipped with support for 3D <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-3d-externalRenderers.html\">externalRenderers<\/a> since its inception in 2016, and custom 2D layers using <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerView2D.html\">BaseLayerView2D<\/a> and <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html\">BaseLayerViewGL2D<\/a> since versions 4.8 and 4.11 respectively. This article focuses on custom WebGL layers, the ones that rely on <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html\">BaseLayerViewGL2D<\/a> for 2D <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-MapView.html\">MapView<\/a> rendering.<\/p>\n<p>There is an official SDK <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/custom-gl-visuals\/\">sample\/tutorial<\/a> that explains how to create custom WebGL layers. In a nutshell, the following steps are involved.<\/p>\n<ul>\n<li>Inherit a custom 2D WebGL layer view from <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html\">BaseLayerViewGL2D<\/a><\/li>\n<li>Override method <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html#attach\">BaseLayerViewGL2D.attach()<\/a> to create WebGL resources and other objects needed <em>before<\/em> rendering starts. This method acts as a constructor and is called once by the <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-MapView.html\">MapView<\/a> when the custom layer is added to the map. A common responsibility of this method is to create WebGL programs, quad meshes, and procedural textures.<\/li>\n<li>Override method <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html#render\">BaseLayerViewGL2D.render()<\/a> to issue draw calls and update WebGL resources that are managed dynamically; examples of dynamic resources are imagery textures and meshes that depend on the level of detail (LOD). This method is called every frame by the <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-MapView.html\">MapView<\/a>.<\/li>\n<li>Override method <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html#detach\">BaseLayerViewGL2D.detach()<\/a> to destroy all resources created by the previous 2 methods. This method acts as a destructor and is called once by the <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-MapView.html\">MapView<\/a> when the custom layer is removed from the map. These 3 methods are the only ones from where WebGL work can be issued; calling WebGL from any other place, including asynchronous callbacks and after <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Operators\/await\">await<\/a>, will likely break rendering for the entire <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-MapView.html\">MapView<\/a>.<\/li>\n<li>At any time, the custom code can invoke <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html#requestRender\">BaseLayerViewGL2D.requestRender()<\/a> to schedule a new frame, which will result in the <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-MapView.html\">MapView<\/a> redrawing all layers, including the custom one, which in turn will cause <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html#render\">BaseLayerViewGL2D.render()<\/a> to run. <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html#requestRender\">BaseLayerViewGL2D.requestRender()<\/a> can be used to implement animations and schedule updates to dynamic WebGL resources.<\/li>\n<li>Inherit a custom layer from <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-layers-Layer.html\">esri\/Layer<\/a> or some other suitable layer class.<\/li>\n<li>Override method <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-layers-Layer.html#createLayerView\">Layer.createLayerView()<\/a> to return a new instance of the previously defined custom WebGL layer view.<\/li>\n<\/ul>\n<p>The following sequence diagram shows the runtime interactions between the app and the other objects involved in 2D rendering.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1531872,"id":1531872,"title":"life-cycle-bw","filename":"life-cycle-bw-1.png","filesize":77073,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-bw-1.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\/life-cycle-bw-2","alt":"","author":"7921","description":"","caption":"","name":"life-cycle-bw-2","status":"inherit","uploaded_to":1502552,"date":"2022-03-25 09:16:18","modified":"2022-03-25 09:16:18","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":2505,"height":2003,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-bw-1-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-bw-1.png","medium-width":326,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-bw-1.png","medium_large-width":768,"medium_large-height":614,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-bw-1.png","large-width":1351,"large-height":1080,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-bw-1-1536x1228.png","1536x1536-width":1536,"1536x1536-height":1228,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-bw-1-2048x1638.png","2048x2048-width":2048,"2048x2048-height":1638,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-bw-1-582x465.png","card_image-width":582,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-bw-1-1351x1080.png","wide_image-width":1351,"wide_image-height":1080}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p>This is the extension experience that we have been supporting for over 10 releases of the API. It could very well be that this is all you need. If that&#8217;s the case, great! We have <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/?tagged=BaseLayerViewGL2D\">plenty of SDK samples<\/a> that cover the standard extension workflow in detail, both for <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html\">BaseLayerViewGL2D<\/a> and <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html\">BaseLayerView2D<\/a>, its Canvas2D counterpart.<\/p>\n<p>But, if you are a WebGL developer and you are looking for ways to boost your productivity, please keep reading!<\/p>\n"},{"acf_fc_layout":"content","content":"<p><strong>An overview of the <code>animated-flow-ts<\/code> repository<\/strong><\/p>\n<p>Let&#8217;s take a look at the code in the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\">animated-flow-ts<\/a> repo and see how it can help your project.<\/p>\n<p>Start by cloning the repository, <code>cd<\/code> into it, <code>npm install<\/code>, and <code>npm start<\/code> it.<\/p>\n"},{"acf_fc_layout":"sidebar","content":"","image_reference":false,"layout":"code_snippet","image_reference_figure":"","snippet":"git clone https:\/\/github.com\/Esri\/animated-flow-ts.git\r\ncd animated-flow-ts\r\nnpm install\r\nnpm start","spotlight_name":"","section_title":"","position":"Right","spotlight_image":false},{"acf_fc_layout":"content","content":"<p>These commands start the TypeScript compiler in <code>--watch<\/code> mode, so that every time that a <code>.ts<\/code> file is modified, it is automatically recompiled. In parallel, a test web server starts listening at <a href=\"http:\/\/localhost:3000\">http:\/\/localhost:3000<\/a>. A browser window should automatically open with a list of demo apps for you to try. The app that we will be using today is called <code>jumpstart<\/code>. If the browser does not open for some reason, launch your browser of choice, and point it to <a href=\"http:\/\/localhost:3000\/demos\/jumpstart.html\">http:\/\/localhost:3000\/demos\/jumpstart.html<\/a>.<\/p>\n<p>The repository uses TypeScript and AMD modules.<\/p>\n<ul>\n<li>The AMD build of the JS API is loaded from the Esri CDN; the JS API also provides the AMD loader itself and the <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/programming-patterns\/#loading-classes\">require()<\/a> function, just like any of our SDK samples.<\/li>\n<li>the AMD build of the <code>gl-matrix<\/code> is also loaded from a CDN;<\/li>\n<li>the local TypeScript files are compiled to AMD modules and loaded.<\/li>\n<\/ul>\n<p>The two top-level directories are <code>demos<\/code> and <code>src<\/code>.<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/demos\">demos<\/a> contains HTML files; each file is an app and, at first sight, it looks just like any of our many SDK samples that load the JS API from the CDN. However, these apps also have a <code>dojoConfig<\/code> section that enables the loading of local JavaScript AMD modules from the <code>demos\/js<\/code> subdirectory. The <code>dojoConfig<\/code> section also enables the loading for a public release of the <code>gl-matrix<\/code> library, which we need for WebGL matrix algebra.<\/li>\n<li><a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/demos\">src<\/a> contains TypeScript files. The <code>tsc<\/code> command compiles them to JavaScript AMD modules, in such a way that each <code>.ts<\/code> file results in a corresponding <code>.js<\/code> file under <code>demos\/js<\/code>; these are the files loaded by the apps.<\/li>\n<\/ul>\n<p>The TypeScript files are organized into 4 packages.<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/apps\">apps<\/a> contains files whose names match the HTML apps in <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/demos\">demos<\/a>. Each demo app is configured to load its corresponding JavaScript module, which acts as the entry point and loads all other required modules.<\/li>\n<li><a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/core\">core<\/a> implements some generic logic that any 2D custom WebGL layer is expected to need, in one form or another.<\/li>\n<li><a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/flow\">flow<\/a> contains the implementation of the flow visualization. It depends on the generic <code>core<\/code> package and adds specific algorithms and techniques that implement the flow visualization.<\/li>\n<li><a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/workers\">workers<\/a> contains code that is designed to run on a <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-core-workers.html\"><em>web worker<\/em><\/a>. The use of workers is optional but can greatly improve the responsiveness of your application.<\/li>\n<\/ul>\n<p><strong>The <code>core<\/code> package<\/strong><\/p>\n<p>The <code>core<\/code> package contains a few modules; 3 of them are relevant for implementing new visualizations.<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/core\/rendering.ts\">core\/rendering<\/a> exports a class named <code>VisualizationStyle<\/code>. It is an abstract class that is used as a base for concrete <em>visualization styles<\/em>. A <em>visualization style<\/em> knows how to load the required data and how to render it. Traditionally, there used to be a single concrete implementation of this class; <code>FlowVisualizationStyle<\/code> in <code>flow\/rendering.ts<\/code> implements all the rendering and programming techniques that are unique to the flow visualization that we originally released last August. In this article, we will spend much of our time describing alternate visualizations, each implemented by its <code>VisualizationStyle<\/code> subclass.<\/li>\n<li><a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/core\/types.ts\">core\/types<\/a> is a declaration file that contains type definitions and interfaces.<\/li>\n<li><a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/core\/view.ts\">core\/view<\/a> subclasses <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html\">BaseLayerViewGL2D<\/a> and exports <code>VisualizationLayerView2D<\/code>. This class functions as an easier starting point for creating custom WebGL layers. In this article, we will create a subclass of <code>VisualizationLayerView2D<\/code> for each of the alternate visualizations that we will be working on.<\/li>\n<\/ul>\n<p><strong>The <code>VisualizationLayerView2D<\/code> abstract base class<\/strong><\/p>\n<p>The abstract class <code>VisualizationLayerView2D<\/code> subclasses <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html\">BaseLayerViewGL2D<\/a> and offers an easier, albeit more constrained, extension workflow. To take advantage of it, subclass <code>VisualizationLayerView2D<\/code> and implement method <code>createVisualizationStyle()<\/code> to return your style object.<\/p>\n<p>You must also flag the custom layer view as animated or not by implementing the <code>animate<\/code> flag. An animated layer view will redraw continuously without the need to explicitly invoke <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html#requestRender\">requestRender()<\/a>.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1525282,"id":1525282,"title":"MyLV","filename":"MyLV-1.png","filesize":11329,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyLV-1.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\/mylv-2","alt":"","author":"7921","description":"","caption":"","name":"mylv-2","status":"inherit","uploaded_to":1502552,"date":"2022-03-22 22:47:07","modified":"2022-03-22 22:47:07","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":807,"height":156,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyLV-1-213x156.png","thumbnail-width":213,"thumbnail-height":156,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyLV-1.png","medium-width":464,"medium-height":90,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyLV-1.png","medium_large-width":768,"medium_large-height":148,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyLV-1.png","large-width":807,"large-height":156,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyLV-1.png","1536x1536-width":807,"1536x1536-height":156,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyLV-1.png","2048x2048-width":807,"2048x2048-height":156,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyLV-1.png","card_image-width":807,"card_image-height":156,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyLV-1.png","wide_image-width":807,"wide_image-height":156}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p><strong>The <code>Resources<\/code> interface<\/strong><\/p>\n<p>The <code>Resources<\/code> interface is defined in <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/core\/types.d.ts\">core\/types<\/a> and describes an object that supports <code>attach(gl)<\/code> and <code>detach(gl)<\/code> methods. Such objects are instantiated by the visualization style in an <em>unattached<\/em> state. The <code>VisualizationLayerView2D<\/code> is responsible for <em>attaching<\/em> them when they are first rendered, and <em>detaching<\/em> them when they are not needed anymore. Methods <code>attach(gl)<\/code> and <code>detach(gl)<\/code> act as a constructor\/destructor pair and receive the current WebGL rendering context as a parameter.<\/p>\n<p>These objects are meant as containers of resources needed by the rendering algorithm, including non-garbage-collectible references such as WebGL objects and WebSockets.<\/p>\n<p>At any given time, a visualization relies on a set of <em>global<\/em> resources and a set of <em>local<\/em> resources. As such, you need to implement the <code>Resources<\/code> interface twice.<\/p>\n<ul>\n<li><em>Global resources<\/em> are only loaded and attached once, when the layer is added to the map, and are detached when the layer is removed; they are independent of the view state and do not need to be reloaded when the map is moved around.<\/li>\n<li><em>Local resources<\/em> are dependent on the view state and are created and destroyed frequently, every time that the view becomes stationary after moving the map.<\/li>\n<\/ul>\n"},{"acf_fc_layout":"image","image":{"ID":1525172,"id":1525172,"title":"MyResources","filename":"MyResources.png","filesize":15684,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyResources.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\/myresources","alt":"","author":"7921","description":"","caption":"","name":"myresources","status":"inherit","uploaded_to":1502552,"date":"2022-03-22 22:32:03","modified":"2022-03-22 22:32:03","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":466,"height":369,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyResources-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyResources.png","medium-width":330,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyResources.png","medium_large-width":466,"medium_large-height":369,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyResources.png","large-width":466,"large-height":369,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyResources.png","1536x1536-width":466,"1536x1536-height":369,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyResources.png","2048x2048-width":466,"2048x2048-height":369,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyResources.png","card_image-width":466,"card_image-height":369,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyResources.png","wide_image-width":466,"wide_image-height":369}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p><strong>The <code>VisualizationStyle<\/code> abstract base class<\/strong><\/p>\n<p>Next, you will need to subclass <code>VisualizationStyle<\/code>. A <em>visualization style<\/em> implements the logic that loads and renders resources.<\/p>\n<p>You will need to override 3 methods. These methods are automatically invoked by the layer view when needed.<\/p>\n<ul>\n<li><code>loadGlobalResources()<\/code> must return a promise to a concrete <code>Resources<\/code> object. This method is only invoked once, when the layer is added to the map.<\/li>\n<li><code>loadLocalResources()<\/code> is similar but it is invoked every time that the view state becomes stationary. It receives information about the view state and the target size in pixels so that it can determine what to load and the appropriate LOD.<\/li>\n<li><code>renderVisualization()<\/code> is invoked every frame and is responsible for issuing the WebGL rendering commands. It receives as inputs the current WebGL rendering context, the global resources, the most recent local resources, and a set of <code>VisualizationRenderParams<\/code>.<\/li>\n<\/ul>\n"},{"acf_fc_layout":"image","image":{"ID":1525602,"id":1525602,"title":"MyVizStyle","filename":"MyVizStyle-1.png","filesize":27807,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyVizStyle-1.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\/myvizstyle-2","alt":"","author":"7921","description":"","caption":"","name":"myvizstyle-2","status":"inherit","uploaded_to":1502552,"date":"2022-03-22 23:31:21","modified":"2022-03-22 23:31:21","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":850,"height":489,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyVizStyle-1-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyVizStyle-1.png","medium-width":454,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyVizStyle-1.png","medium_large-width":768,"medium_large-height":442,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyVizStyle-1.png","large-width":850,"large-height":489,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyVizStyle-1.png","1536x1536-width":850,"1536x1536-height":489,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyVizStyle-1.png","2048x2048-width":850,"2048x2048-height":489,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyVizStyle-1-808x465.png","card_image-width":808,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyVizStyle-1.png","wide_image-width":850,"wide_image-height":489}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p><strong>The <code>VisualizationRenderParams<\/code> interface<\/strong><\/p>\n<p>An instance of this type is delivered every frame to the <code>VisualizationStyle.renderVisualization()<\/code> method. It tells the visualization style <em>where<\/em> to render the visualization. Here is a list of its most useful properties.<\/p>\n<ul>\n<li><code>size: [number, number]<\/code>. The size of the <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-MapView.html\">MapView<\/a>, in pixels.<\/li>\n<li><code>translation: [number, number]<\/code>. Where the upper-left corner of the rendered visualization should appear, in pixels.<\/li>\n<li><code>rotation: number<\/code>. The rotation of the view, in radians.<\/li>\n<li><code>scale: number<\/code>. The scale factor for the visualization. A value of 1 means that the visualization is being rendered at the same scale at which its data was loaded.<\/li>\n<\/ul>\n"},{"acf_fc_layout":"content","content":"<p><strong>Creating the custom layer class<\/strong><\/p>\n<p>The <code>core<\/code> package does not prescribe any particular way to implement the custom layer. As such, the most common way to go about it is to subclass <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-layers-Layer.html\">esri\/Layer<\/a> or some other suitable layer type and override <code>createLayerView()<\/code> to return an instance of the custom layer view.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1525272,"id":1525272,"title":"MyL","filename":"MyL-1.png","filesize":9101,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyL-1.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\/myl-2","alt":"","author":"7921","description":"","caption":"","name":"myl-2","status":"inherit","uploaded_to":1502552,"date":"2022-03-22 22:47:04","modified":"2022-03-22 22:47:04","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":439,"height":268,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyL-1-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyL-1.png","medium-width":428,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyL-1.png","medium_large-width":439,"medium_large-height":268,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyL-1.png","large-width":439,"large-height":268,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyL-1.png","1536x1536-width":439,"1536x1536-height":268,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyL-1.png","2048x2048-width":439,"2048x2048-height":268,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyL-1.png","card_image-width":439,"card_image-height":268,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/MyL-1.png","wide_image-width":439,"wide_image-height":268}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p><strong>An alternate extension mechanism<\/strong><\/p>\n<p>Let&#8217;s briefly recap the steps needed to implement a custom WebGL layer by extending <code>VisualizationLayerView2D<\/code> and the other classes in the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/core\">core<\/a> package of the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\">animated-flow-ts<\/a> repository. Please compare and contrast with the steps taken when extending <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html\">BaseLayerViewGL2D<\/a>.<\/p>\n<ul>\n<li>Decide what kind of resources you need at render time; these could be programs, textures, static or dynamic meshes.<\/li>\n<li>Implement the <code>Resources<\/code> interface twice: first, as a <em>global<\/em> resources object, holding onto resources needed at all times and which are only loaded once; second, as a <em>local<\/em> resources object, holding onto resources which are reloaded every time that the map is moved around.<\/li>\n<li>Extend <code>VisualizationStyle<\/code>. You need to implement 3 methods; <code>loadGlobalResources()<\/code>, <code>loadLocalResources()<\/code>, and <code>renderVisualization()<\/code>.<\/li>\n<li>Extend <code>VisualizationLayerView2D<\/code>. You need to implement <code>createVisualizationStyle()<\/code> and the <code>animate<\/code> flag.<\/li>\n<li>Extend <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-layers-Layer.html\">esri\/Layer<\/a> and implement <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-layers-Layer.html#createLayerView\">createLayerView()<\/a> as usual.<\/li>\n<\/ul>\n<p>As you can see, usage of the <code>core<\/code> package imposes a lot of structure on the extending code; developers <em>have<\/em> to implement certain things in a certain way; the standard <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-2d-layers-BaseLayerViewGL2D.html\">BaseLayerViewGL2D<\/a> extension experience, on the other hand, offers much more freedom.<\/p>\n<p>However, there are two benefits to choosing to extend the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/core\">core<\/a> package; these cover two aspects of custom layer development that are otherwise pretty difficult to get right.<\/p>\n<p><strong>Benefit #1: Automatic resource life cycle management<\/strong><\/p>\n<p>Your custom layer view inherits from <code>VisualizationLayerView2D<\/code> a complete resource management system. You will not have to load, queue, attach, detach, listen, watch, debounce, anything. You just have to split the rendering resources between global and local, implement <code>attach(gl)<\/code> and <code>detach(gl)<\/code>, and the base classes in <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/core\">core<\/a> will do the rest.<\/p>\n<p>This automatic behavior is exemplified by the sequence diagram below.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1531882,"id":1531882,"title":"life-cycle-styles-bw","filename":"life-cycle-styles-bw-1.png","filesize":200822,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-styles-bw-1.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\/life-cycle-styles-bw-2","alt":"","author":"7921","description":"","caption":"","name":"life-cycle-styles-bw-2","status":"inherit","uploaded_to":1502552,"date":"2022-03-25 09:16:26","modified":"2022-03-25 09:16: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":3953,"height":3699,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-styles-bw-1-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-styles-bw-1.png","medium-width":279,"medium-height":261,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-styles-bw-1.png","medium_large-width":768,"medium_large-height":719,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-styles-bw-1.png","large-width":1154,"large-height":1080,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-styles-bw-1-1536x1437.png","1536x1536-width":1536,"1536x1536-height":1437,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-styles-bw-1-2048x1916.png","2048x2048-width":2048,"2048x2048-height":1916,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-styles-bw-1-497x465.png","card_image-width":497,"card_image-height":465,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/life-cycle-styles-bw-1-1154x1080.png","wide_image-width":1154,"wide_image-height":1080}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p><strong>Benefit #2: Simplified view state management<\/strong><\/p>\n<p>When the view becomes stationary and <code>VisualizationStyle.loadLocalResources()<\/code> is called, it receives as parameters the extent of the data to load, and the target size in pixels. You can use this information to query the data at the appropriate LOD and return the ideal set of local resources for it. When this happens, the <code>translation<\/code> and <code>scale<\/code> passed to <code>VisualizationStyle.renderVisualization()<\/code> are reset.<\/p>\n<p>The diagram below illustrates the behavior for the case of zooming in and subsequent data reload at a higher LOD.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1531862,"id":1531862,"title":"simplified-view-state-bw","filename":"simplified-view-state-bw-2.png","filesize":37354,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/simplified-view-state-bw-2.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\/simplified-view-state-bw-3","alt":"","author":"7921","description":"","caption":"","name":"simplified-view-state-bw-3","status":"inherit","uploaded_to":1502552,"date":"2022-03-25 09:16:12","modified":"2022-03-25 09:16:12","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":2254,"height":512,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/simplified-view-state-bw-2-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/simplified-view-state-bw-2.png","medium-width":464,"medium-height":105,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/simplified-view-state-bw-2.png","medium_large-width":768,"medium_large-height":174,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/simplified-view-state-bw-2.png","large-width":1920,"large-height":436,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/simplified-view-state-bw-2-1536x349.png","1536x1536-width":1536,"1536x1536-height":349,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/simplified-view-state-bw-2-2048x465.png","2048x2048-width":2048,"2048x2048-height":465,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/simplified-view-state-bw-2-826x188.png","card_image-width":826,"card_image-height":188,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/simplified-view-state-bw-2-1920x436.png","wide_image-width":1920,"wide_image-height":436}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p>What we just described is a very important inherited behavior; it implements data refreshing and accurate screen positioning; it is a fundamental component of any real-world large-scale data visualization. Crucially, it enables your WebGL code to support multiple LODs while at the same time keeping your attributes and uniforms as small in magnitude as possible; remember that WebGL shaders use single-precision floating-point (or worse) for all computations, and when attributes or uniforms get too large, <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/sample-code\/custom-gl-visuals\/#dealing-with-precision-issues\">positioning tends to get very imprecise<\/a>.<\/p>\n<p>Please take some time to watch the video below; it illustrates the mechanism in action. See how the idle values for <code>translation<\/code> and <code>scale<\/code> are reset every time that <code>loadLocalResources()<\/code> completes.<\/p>\n<p>The idle values for <code>translation<\/code> are negative because the origin of the visualization is North-West of the upper left corner of the current extent; this happens because <code>VisualizationLayerView2D<\/code> tells the visualization style to load an extent that is <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/core\/settings.ts#L23\">15% larger than what is needed to cover the screen<\/a>. We do this to have some headroom and keep border artifacts away from the area of interest.<\/p>\n"},{"acf_fc_layout":"youtube","start_time":"0","end_time":"","youtube_video_url":"<iframe title=\"State reset behavior\" width=\"520\" height=\"390\" src=\"https:\/\/www.youtube.com\/embed\/ZzDgk55IGoQ?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>"},{"acf_fc_layout":"content","content":"<p>Our overview of the repository is complete. Now it&#8217;s time to look at 3 brand-new custom visualizations and their implementations! You can find the new custom layers, together with their layer views and visualization styles, in the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/jumpstart\">src\/jumpstart<\/a> folder.<\/p>\n<p>There is an app (<a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/apps\/jumpstart.ts\">.ts<\/a>, <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/demos\/jumpstart.html\">.html<\/a>) that adds the 3 custom layers to the <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-MapView.html\">MapView<\/a> and we recommend that you keep it open in your browser as you experiment with the code. If you run <code>npm start<\/code>, a live version of the app should open automatically at <a href=\"http:\/\/localhost:3000\/demos\/jumpstart.html\">http:\/\/localhost:3000\/demos\/jumpstart.html<\/a>. A live version is also <a href=\"https:\/\/wind-es.s3.us-west-1.amazonaws.com\/demos\/jumpstart.html\">available online<\/a>.<\/p>\n<p>For each visualization, check out the <code>layer.ts<\/code> file, which contains the custom layer, and the <code>view.ts<\/code> file, which contains the custom layer view. See how the layer overrides <code>createLayerView()<\/code> and how the layer view inherits from <code>VisualizationLayerView2D<\/code> and overrides <code>createVisualizationStyle()<\/code>.<\/p>\n<p>Then check the <code>rendering.ts<\/code> file which defines the global resources, the local resources, and the visualization style. All WebGL rendering code resides here. Please note that visualization styles do not require a layer view, and can be instantiated and used independently from the <a href=\"https:\/\/developers.arcgis.com\/javascript\/latest\/api-reference\/esri-views-MapView.html\">MapView<\/a>; this can be useful for testing and for generating thumbnails and galleries.<\/p>\n<p>One thing that all the visualization styles in the repo have in common, is the way the transform matrices are handled. For starters, they all use two matrices named <code>u_ScreenFromLocal<\/code> and <code>u_ClipFromScreen<\/code>, their uniform locations are always kept in the global resources object, and the actual <code>mat4<\/code> instances are always kept in the local resources object. In addition to this, their computation is always done in the same way.<\/p>\n<p>Here is how <code>u_ScreenFromLocal<\/code> is computed.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1532032,"id":1532032,"title":"ScreenFromLocal","filename":"ScreenFromLocal.png","filesize":28499,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ScreenFromLocal.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\/screenfromlocal","alt":"","author":"7921","description":"","caption":"","name":"screenfromlocal","status":"inherit","uploaded_to":1502552,"date":"2022-03-25 12:16:10","modified":"2022-03-25 12:16:10","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":887,"height":227,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ScreenFromLocal-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ScreenFromLocal.png","medium-width":464,"medium-height":119,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ScreenFromLocal.png","medium_large-width":768,"medium_large-height":197,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ScreenFromLocal.png","large-width":887,"large-height":227,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ScreenFromLocal.png","1536x1536-width":887,"1536x1536-height":227,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ScreenFromLocal.png","2048x2048-width":887,"2048x2048-height":227,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ScreenFromLocal-826x211.png","card_image-width":826,"card_image-height":211,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ScreenFromLocal.png","wide_image-width":887,"wide_image-height":227}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p>And here is how <code>u_ClipFromScreen<\/code> is computed.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1532022,"id":1532022,"title":"ClipFromScreen","filename":"ClipFromScreen.png","filesize":25548,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ClipFromScreen.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\/clipfromscreen","alt":"","author":"7921","description":"","caption":"","name":"clipfromscreen","status":"inherit","uploaded_to":1502552,"date":"2022-03-25 12:16:06","modified":"2022-03-25 12:16: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":793,"height":140,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ClipFromScreen-213x140.png","thumbnail-width":213,"thumbnail-height":140,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ClipFromScreen.png","medium-width":464,"medium-height":82,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ClipFromScreen.png","medium_large-width":768,"medium_large-height":136,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ClipFromScreen.png","large-width":793,"large-height":140,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ClipFromScreen.png","1536x1536-width":793,"1536x1536-height":140,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ClipFromScreen.png","2048x2048-width":793,"2048x2048-height":140,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ClipFromScreen.png","card_image-width":793,"card_image-height":140,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/ClipFromScreen.png","wide_image-width":793,"wide_image-height":140}},"image_position":"center","orientation":"horizontal","hyperlink":""},{"acf_fc_layout":"content","content":"<p><strong>Code sample #1: The view state &#8220;test pattern&#8221;<\/strong><\/p>\n<p>This visualization displays the extent passed to <code>loadLocalResources()<\/code> as a translucent rectangle with a dashed border. It also displays the bounds of the extent in map units.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1523912,"id":1523912,"title":"sample-1","filename":"sample-1.png","filesize":173266,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-1.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\/sample-1","alt":"","author":"7921","description":"","caption":"","name":"sample-1","status":"inherit","uploaded_to":1502552,"date":"2022-03-22 18:47:40","modified":"2022-03-22 18:47: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":1907,"height":909,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-1-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-1.png","medium-width":464,"medium-height":221,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-1.png","medium_large-width":768,"medium_large-height":366,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-1.png","large-width":1907,"large-height":909,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-1-1536x732.png","1536x1536-width":1536,"1536x1536-height":732,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-1.png","2048x2048-width":1907,"2048x2048-height":909,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-1-826x394.png","card_image-width":826,"card_image-height":394,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-1.png","wide_image-width":1907,"wide_image-height":909}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/jumpstart\/01-testpattern"},{"acf_fc_layout":"content","content":"<p>Some notes about the implementation.<\/p>\n<ul>\n<li>The <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/01-testpattern\/rendering.ts#L7-L9\">shader program, with its uniform locations, and the quad<\/a> used to render the extent rectangle, are global resources.<\/li>\n<li>The quad is defined using <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/01-testpattern\/rendering.ts#L63\">binary coordinates<\/a>; at render time, the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/01-testpattern\/rendering.ts#L166\">original size<\/a> as <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/01-testpattern\/rendering.ts#L127\">received by loadLocalResources()<\/a> is combined with the binary coordinates in the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/01-testpattern\/rendering.ts#L23\">vertex shader<\/a> so that the quad mesh is &#8220;inflated&#8221; to the appropriate size, and can be <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/01-testpattern\/rendering.ts#L24-L25\">positioned on screen using the <code>u_ScreenFromLocal<\/code> and <code>u_ClipFromScreen<\/code> matrices<\/a>.<\/li>\n<li>The <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/01-testpattern\/rendering.ts#L77\">texture applied to the quad is a local resource<\/a> because it depicts the extent rectangle with its bounds in map units, and the bounds change when the data is reloaded.<\/li>\n<li>Canvas2D is used to <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/01-testpattern\/rendering.ts#L84-L101\">generate an image<\/a> that is then <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/01-testpattern\/rendering.ts#L104-L113\">uploaded to a WebGL texture<\/a>.<\/li>\n<\/ul>\n"},{"acf_fc_layout":"content","content":"<p><strong>Code sample #2: The fancy animated raster<\/strong><\/p>\n<p>This visualization loads elevation data from an imagery tiled service and animates the opacity of each pixel using a formula that combines the current time, the elevation of the pixel, and a sine function.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1523932,"id":1523932,"title":"sample-2","filename":"sample-2.png","filesize":674386,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-2.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\/sample-2","alt":"","author":"7921","description":"","caption":"","name":"sample-2","status":"inherit","uploaded_to":1502552,"date":"2022-03-22 18:47:47","modified":"2022-03-22 18:47:47","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":1913,"height":895,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-2-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-2.png","medium-width":464,"medium-height":217,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-2.png","medium_large-width":768,"medium_large-height":359,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-2.png","large-width":1913,"large-height":895,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-2-1536x719.png","1536x1536-width":1536,"1536x1536-height":719,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-2.png","2048x2048-width":1913,"2048x2048-height":895,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-2-826x386.png","card_image-width":826,"card_image-height":386,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-2.png","wide_image-width":1913,"wide_image-height":895}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/jumpstart\/02-fancyraster"},{"acf_fc_layout":"content","content":"<p>Some notes about the implementation.<\/p>\n<ul>\n<li>The <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/rendering.ts#L8-L10\">shader program, with its uniform locations, and the quad<\/a> used to render the extent rectangle, are global resources.<\/li>\n<li>The quad is defined using <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/rendering.ts#L67\">binary coordinates<\/a>; at render time, the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/rendering.ts#L203\">original size<\/a> of the image <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/rendering.ts#L164\">received by loadLocalResources()<\/a> is combined with the binary coordinates in the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/rendering.ts#L24\">vertex shader<\/a> so that the quad mesh is &#8220;inflated&#8221; to the appropriate size, and can be <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/rendering.ts#L25-L26\">positioned on screen using the <code>u_ScreenFromLocal<\/code> and <code>u_ClipFromScreen<\/code> matrices<\/a>.<\/li>\n<li>The <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/rendering.ts#L81\">texture applied to the quad is a local resource<\/a> because it must be <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/rendering.ts#L135\">reloaded using an instance of ImageryTileLayer every time that <code>loadLocalResources()<\/code> is called<\/a>.<\/li>\n<li>After the imagery has been <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/rendering.ts#L93-L95\">uploaded<\/a> to the texture in <code>LocalResources.attach(gl)<\/code>, the image data itself is <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/rendering.ts#L101-L102\">dropped by nulling<\/a> the reference.<\/li>\n<li>The URL of the imagery service to use <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/layer.ts#L8\">is stored as a property on the layer<\/a>.<\/li>\n<li>The layer view <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/view.ts#L14\">passes it down<\/a> to the created visualization style.<\/li>\n<li>The layer view <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/view.ts#L9\">flags itself as animated<\/a> because the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/rendering.ts#L39\">fragment shader is time-dependent<\/a> and the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/02-fancyraster\/rendering.ts#L204\">renderVisualization() method uses the current time<\/a>.<\/li>\n<\/ul>\n"},{"acf_fc_layout":"content","content":"<p><strong>Code sample #3: The shimmering power plants<\/strong><\/p>\n<p>This visualization loads points from a feature layer and renders them as shimmering luminous dots. The color of the dot is attribute-driven, and the falloff of the opacity is animated using a sine function.<\/p>\n"},{"acf_fc_layout":"image","image":{"ID":1523902,"id":1523902,"title":"sample-3","filename":"sample-3.png","filesize":286995,"url":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-3.png","link":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\/sample-3","alt":"","author":"7921","description":"","caption":"","name":"sample-3","status":"inherit","uploaded_to":1502552,"date":"2022-03-22 18:47:31","modified":"2022-03-22 18:47:31","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":1911,"height":911,"sizes":{"thumbnail":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-3-213x200.png","thumbnail-width":213,"thumbnail-height":200,"medium":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-3.png","medium-width":464,"medium-height":221,"medium_large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-3.png","medium_large-width":768,"medium_large-height":366,"large":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-3.png","large-width":1911,"large-height":911,"1536x1536":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-3-1536x732.png","1536x1536-width":1536,"1536x1536-height":732,"2048x2048":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-3.png","2048x2048-width":1911,"2048x2048-height":911,"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-3-826x394.png","card_image-width":826,"card_image-height":394,"wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/sample-3.png","wide_image-width":1911,"wide_image-height":911}},"image_position":"center","orientation":"horizontal","hyperlink":"https:\/\/github.com\/Esri\/animated-flow-ts\/tree\/main\/src\/jumpstart\/03-shimmer"},{"acf_fc_layout":"content","content":"<p>Some notes about the implementation.<\/p>\n<ul>\n<li>The <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/03-shimmer\/rendering.ts#L9-L10\">shader program<\/a>, with its uniform locations, are global resources.<\/li>\n<li>The <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/03-shimmer\/rendering.ts#L81-L83\">mesh that represents all markers is a local resource<\/a> because the features must be <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/03-shimmer\/rendering.ts#L147\">fetched again<\/a>, and the WebGL buffers <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/03-shimmer\/rendering.ts#L162-L200\">rewritten<\/a>, every time that <code>loadLocalResources()<\/code> is called.<\/li>\n<li>After the mesh data has been <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/03-shimmer\/rendering.ts#L97-L106\">uploaded<\/a> to the buffers in <code>LocalResources.attach(gl)<\/code>, the typed arrays are <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/03-shimmer\/rendering.ts#L109-L110\">dropped by nulling<\/a> the references.<\/li>\n<li>The layer <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/03-shimmer\/layer.ts#L8-L18\">has several configuration properties<\/a>.<\/li>\n<li>The layer view <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/03-shimmer\/view.ts#L35\">passes them down<\/a> to the created visualization style.<\/li>\n<li>The layer view <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/03-shimmer\/view.ts#L9\">flags itself as animated<\/a> because the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/03-shimmer\/rendering.ts#L45\">fragment shader is time-dependent<\/a> and the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\/blob\/main\/src\/jumpstart\/03-shimmer\/rendering.ts#L243\">renderVisualization() method uses the current time<\/a>.<\/li>\n<\/ul>\n"},{"acf_fc_layout":"content","content":"<p><strong>Conclusions<\/strong><\/p>\n<p>We hope that you enjoyed this deep dive into the world of custom WebGL layers and that you will find the <a href=\"https:\/\/github.com\/Esri\/animated-flow-ts\">animated-flow-ts<\/a> repository useful for your project.<\/p>\n<p><em>Clone it! Study it! Mod it! Ship it!<\/em><\/p>\n<p>Go build something amazing and share your experience on <a href=\"https:\/\/community.esri.com\/t5\/arcgis-api-for-javascript-blog\/streamlines-and-flow-animation-in-the-arcgis-api\/ba-p\/1094235\">the animated-flow-ts thread on community.esri.com<\/a>!<\/p>\n<p><em>Happy coding!<\/em><\/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":1322972,"post_author":"7921","post_date":"2021-08-31 17:30:08","post_date_gmt":"2021-09-01 00:30:08","post_content":"","post_title":"Visualize and animate flow in MapView with a custom WebGL layer","post_excerpt":"","post_status":"publish","comment_status":"open","ping_status":"closed","post_password":"","post_name":"visualize-and-animate-flow-in-mapview-with-a-custom-webgl-layer","to_ping":"","pinged":"","post_modified":"2024-04-12 03:59:02","post_modified_gmt":"2024-04-12 10:59:02","post_content_filtered":"","post_parent":0,"guid":"https:\/\/www.esri.com\/arcgis-blog\/?post_type=blog&#038;p=1322972","menu_order":0,"post_type":"blog","post_mime_type":"","comment_count":"0","filter":"raw"}],"card_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/BlogCard.png","wide_image":"https:\/\/www.esri.com\/arcgis-blog\/app\/uploads\/2022\/03\/BlogWide.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>Jumpstart your visualization: build on top of an existing custom 2D 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\/jumpstart-your-custom-layer\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Jumpstart your visualization: build on top of an existing custom 2D layer\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-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:46:07+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\/jumpstart-your-custom-layer#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\"},\"author\":{\"name\":\"Dario D'Amico\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/94907a22d3e4a02e7c0cb7a100917434\"},\"headline\":\"Jumpstart your visualization: build on top of an existing custom 2D layer\",\"datePublished\":\"2022-03-31T17:45:31+00:00\",\"dateModified\":\"2024-04-12T10:46:07+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\"},\"wordCount\":12,\"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\/jumpstart-your-custom-layer#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\",\"url\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\",\"name\":\"Jumpstart your visualization: build on top of an existing custom 2D layer\",\"isPartOf\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/#website\"},\"datePublished\":\"2022-03-31T17:45:31+00:00\",\"dateModified\":\"2024-04-12T10:46:07+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.esri.com\/arcgis-blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Jumpstart your visualization: build on top of an existing custom 2D 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":"Jumpstart your visualization: build on top of an existing custom 2D 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\/jumpstart-your-custom-layer","og_locale":"en_US","og_type":"article","og_title":"Jumpstart your visualization: build on top of an existing custom 2D layer","og_url":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer","og_site_name":"ArcGIS Blog","article_publisher":"https:\/\/www.facebook.com\/esrigis\/","article_modified_time":"2024-04-12T10:46:07+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\/jumpstart-your-custom-layer#article","isPartOf":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer"},"author":{"name":"Dario D'Amico","@id":"https:\/\/www.esri.com\/arcgis-blog\/#\/schema\/person\/94907a22d3e4a02e7c0cb7a100917434"},"headline":"Jumpstart your visualization: build on top of an existing custom 2D layer","datePublished":"2022-03-31T17:45:31+00:00","dateModified":"2024-04-12T10:46:07+00:00","mainEntityOfPage":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer"},"wordCount":12,"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\/jumpstart-your-custom-layer#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer","url":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer","name":"Jumpstart your visualization: build on top of an existing custom 2D layer","isPartOf":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/#website"},"datePublished":"2022-03-31T17:45:31+00:00","dateModified":"2024-04-12T10:46:07+00:00","breadcrumb":{"@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.esri.com\/arcgis-blog\/products\/js-api-arcgis\/developers\/jumpstart-your-custom-layer#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.esri.com\/arcgis-blog\/"},{"@type":"ListItem","position":2,"name":"Jumpstart your visualization: build on top of an existing custom 2D 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":"March 31, 2022","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\/2022\/03\/BlogWide.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":422,"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":213,"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":362,"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\/1502552","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=1502552"}],"version-history":[{"count":0,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/blog\/1502552\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/media?parent=1502552"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/categories?post=1502552"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/tags?post=1502552"},{"taxonomy":"industry","embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/industry?post=1502552"},{"taxonomy":"product","embeddable":true,"href":"https:\/\/www.esri.com\/arcgis-blog\/wp-json\/wp\/v2\/product?post=1502552"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}