GIS Blog - Page 2

Showing results for 
Show  only  | Search instead for 
Did you mean: 

Latest Activity

(449 Posts)
Esri Contributor

Typically the Feature Form widget is used within a side panel, like in this sample.

But what if you want a slightly different workflow where the user draws their boundary and then is prompted with a popup to fill out. We can combine the Sketch widget and the Feature Form widget within a Modal for that effect:


1 0 457
Esri Regular Contributor

A great post from @AndrewSkinner  about using the new blending tools in Map Viewer Beta. Another good reason to start working with Map Viewer Beta if you haven't found enough reasons already.


0 0 428
Esri Regular Contributor

On August 4, 2020, a massive explosion gutted buildings and crumbled neighborhoods surrounding the port of Beirut, Lebanon, after a warehouse containing 2,750 tons of ammonium nitrate ignited. Maxar published imagery post-blast, and a swipe app was created to view before and after.


0 0 750
MVP Regular Contributor

At first glance, the two look so familiar (at least to me).

Yet per the documentation:

A record set is of the 'dataset' parameter data type, is transportable, and supports the User defined value input mode for GP task settings.

The schema of a record set contains the definition of one or more fields:

When the model tool dialog box is opened, records and attributes can be entered.

A quick tour of using Feature Set and Record Set—Help | ArcGIS Desktop 

A Value Table is a parameter of a GP tool that uses a multicolumn table of the 'Value Tables', are non-transportable, and their Input mode will be fixed to Constant value for GP task settings.

The task will use the value you supplied for the parameter when you created the GP result.

it will not become a task parameter when the service is published.

If you need your client to enter values rather than using the constant value, you'll need to modify your model or script so that it uses other data types besides Value Table.

Input modes and parameter data types—Documentation | ArcGIS Enterprise 

0 0 186
Esri Regular Contributor

Unlike other Esri products, ArcGIS Living Atlas of the World is truly many moving parts continuously updating independently on their own schedule. Living Atlas News is an attempt to capture what's been going on. Check it out here: ArcGIS Living Atlas News (July 2020).


0 0 287
Esri Regular Contributor

The 2020 Virtual Esri International User Conference starts Monday, July 13, 2020.  I hope to virtually meet you there.  If you would like to connect, here are some opportunities:

  • Incorporating Business Value in Your GIS Strategy - Streaming Live, Tuesday, July 14, 2020, 1:40 PM - 2:40 PM PDT
    • Aligning your GIS strategy with your Business goals and objectives will allow you to more effectively address the most pressing needs of your organization. But how do you do that? On top of this, business leaders don’t usually speak in the “language of GIS”, which challenges you to interpret business challenges, and translate them into GIS opportunities. This session will cover strategies for gaining leadership sponsorship, how to determine and measure the business value of your GIS, and considerations for how to communicate that value throughout your organization
  • Two On-Demand Sessions:
    • Evangelizing GIS
      • Getting your workforce to adopt new technology and new workflows requires winning hearts and minds across the organization. Learn how the right communication strategy creates a common vision and momentum to accomplish positive change.
    • Communicating the Value of GIS in Your Organization: How to Measure Return on Investment
      • Understanding, documenting, and communicating the value GIS brings to your organization can be critical to your success. First, presenting the return on investment (ROI) from your programs’ and projects helps celebrate the success of the departments using GIS. This process oftentimes stimulates ideas across an organization as to the uses of spatial thinking and GIS technology. Secondly, it reinforces the investment in technology, people, and equipment to improve government services. Quantified ROI is good to have on hand during budgeting periods, too. Finally, measuring the real benefits of GIS can act as an accountability tool for yourself. It provides a self-check as to whether your program is in sync with or meeting organizational goals. This session will seek to provide guidance on documenting and reporting the business value of GIS. Topics will include Measuring the benefits of GIS; Calculation methods; Examples of success; and Techniques for communicating success.
  • In the Expo - I will be working in the Esri Showcase at the Smarter Government Starts Here booth in the Executive Engagement & Public Policy section.  I am scheduled to be there the following days/times:
    • Monday, July 13, 2020, 12:00 PM - 1:30 PM PDT & 3:00 PM - 4:00 PM PDT
    • Tuesday, July 14, 2020, 7:30 AM - 10:00 AM PDT, 11:00 AM - 12:00 PM PDT & 3:00 PM - 4:00 PM PDT
    • Wednesday, July 15, 2020, 7:30 AM - 10:00 AM PDT & 11:00 AM - 4:00 PM PDT
  • We can also connect using the Networking portion of the platform
  • And of course I am available on LinkedIn and Twitter as well as here on GeoNet

Hope to "see" you there!

0 0 853
MVP Regular Contributor 

Setting x, y and zoom level in url

to retain map view

When you zoom in on a particular location on a map, you sometimes wish you could retain that map view, e.g. to share it with someone else. That's why we were looking for a way to get the x, y and zoom level from the search parameters in our url. But not only do we want to be able to read the values from the query string (i.e. everything behind the question mark), we also want to be able to set them, and to modify them whenever the map view changes, i.e. when the user pans and zooms around on the map.

Below is a description of the functionality we use to grab the x an y coordinates (in British National Grid, rounded to the full meter) and to modify the url.

URLSearchParams and History.replaceState()

The URLSearchParams interface defines utility methods to work with the query string of a URL. In the example below we use the following methods of this interface:

  • The has() method indicates whether a parameter with the specified name exists
  • The get() method returns the (first) value associated to the given search parameter
  • The set() method sets the value associated with a given search parameter. And very important: if the search parameter does not yet exist (which might initially be the case), this method will create the search parameter.

The History.replaceState() method modifies the current history entry. This method is particularly useful when you want to update the URL of the current history entry in response to some user action, e.g. the panning and zooming mentioned before.

And this is how it works:

If you provide the url without any parameters, you will start at the Default map view. But as you will see, the default parameters are added immediately to the address bar:

And as soon as you start zooming and panning around, these parameters will be adjusted every time the view becomes "stationary".

In this way, you can zoom in to a particular spot and copy the url to make sure to share exactly that map view.

If you click on the link below you will immediately end up in Steòrnabhagh  (or Stornoway), on the island of Lewis and Harris in the Outer Hebrides: 

Happy coding!

The code

<!DOCTYPE html>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
  <title>ArcGIS JavaScript with UK data - URL Parameters: x, y and zoomlevel</title>
  <link rel="stylesheet" href="">
  <script src=""></script>
    html, body, #viewDiv {
      padding: 0;
      margin: 0;
      height: 100%;
      width: 100%;
      ], function(Map, Point, MapView, Home, Viewpoint, watchUtils) {

      var sr = 27700;
      var xCoordHome = 500000;
      var yCoordHome = 500000;
      var zoomLevelHome = 7;
      var scaleHome = 5000000;

      var xCoord = xCoordHome;
      var yCoord = yCoordHome;
      var zoomLevel = zoomLevelHome;

      if ('URLSearchParams' in window) { // Feature detection (URLSearchParams is not supported by Internet Explorer)
        var url = new URL(document.URL);
        var search_params = url.searchParams;
        // get searchparams if x, y and l are provided in url
        if(search_params.has('x') && search_params.has('y') && search_params.has('l')) {
          var xCoord = parseInt(search_params.get('x'));
          var yCoord = parseInt(search_params.get('y'));
          var zoomLevel = parseInt(search_params.get('l'));

      var map = new Map({
        basemap: {
          portalItem: {
            id: "0bd3a4a6fd674a90a7d0a9e5f36fb59b" // OS Open Carto

      var view = new MapView({
        spatialReference: sr, 
        container: "viewDiv",
        map: map,
        center: new Point({x: xCoord, y: yCoord, spatialReference: sr}),
        zoom: zoomLevel

      var home = new Home({
        view: view,
        // viewpoint of home needs to be defined (hard coded) because otherwise this home depends on the x, y and l values in the searchparams
        viewpoint: new Viewpoint({targetGeometry: new Point({x: xCoordHome, y: yCoordHome, spatialReference: sr}), scale: scaleHome})

      view.ui.add(home, "top-left")

      watchUtils.whenTrue(view, "stationary", function() {

      //modify url with new coords and zoomlevel as searchparams
      function modifySearchParams() {
        if ('URLSearchParams' in window) { // Feature detection (URLSearchParams is not supported by Internet Explorer)
          xCoordNew = parseInt(;
          yCoordNew = parseInt(;
          zoomLevelNew = view.zoom;
          search_params.set('x', xCoordNew);
          search_params.set('y', yCoordNew);
          search_params.set('l', zoomLevelNew);
 = search_params.toString(); // change the search property of the main url
          new_url = url.toString(); // the new url string
          history.replaceState(null, '', new_url);
  <div id="viewDiv"></div>

0 0 203
Esri Contributor

In part 1 of the series, we used TSDX to create a TypeScript+React Component, installed it into the Experience Builder client folder, and included the Component in a custom widget. The widget did not actually do anything though. In this post, we'll go through how to using ArcGIS API for JavaScript within the component, how to interact with the Map object, and how to pass in configuration settings from Experience Builder.

Create a Map in the TSDX Example Page

Right now, the Example App (what shows when you run npm start in the example folder) is totally empty except for your widget. In an Experience Builder app, your widget will most likely be interacting with other widgets like the Map widget. To mirror this situation in the Example App, we need to include the ArcGIS API for JavaScript directly and create a map object on the page.

Lets start in the example directory of your TSDX project. Within there, the structure of components will be:

  • /example
    • index.tsx - already existing main file. We'll update this to contain the Map and Wrapper (below).
    • /components
      • Map.js - a React component responsible for creating and displaying the ArcGIS API for JavaScript Map object. Pass in a function (viewReady) to be called when the map is ready and can pass a reference to the View back up to index.tsx.
      • Wrapper.js - The equivalent to our custom Experience Builder widget, in that it "wraps" our custom component. index.tsx passes a reference to the mapView that it gets from Map.js.


To include the ArcGIS API for JavaScript map on the page, we will use the esri-loader and esri-loader-hooks packages. To install these dependencies, browse to the example directory in your terminal then run: npm install --save esri-loader esri-loader-hooks

The entire contents of Map.js can be found here. At a high level, we're creating a DOM Node Reference, passing it to useMap (from the esri-loader-hooks library) to create the map, and using useWatch (also from the esri-loader-hooks library). Within the onUpdateChange function, the line const [FeatureLayer] = await loadModules(... is loading the FeatureLayer module from the ArcGIS API for JavaScript so we can use it to create a layer and add to the map.

Finally, at the end of the useWatch function, we call viewReady(view) as a "callback" to the parent component to give it a reference to the MapView (view variable) that we created here in this component.


In index.tsx, we're importing the two components Map and Wrapper and including them in the JSX. The mapView is passed from the Map component to the Wrapper component via React state (see the setMapView call in the function that is passed as a prop to the Map component).

Also in index.tsx, we've created a CSS file and are importing it - basically making the map the full size of the page and hovering the widget over the top of it.


Wrapper.js is the equivalent of the custom Experience Builder wrapper widget that we will build. It is a light-weight "wrapper" and includes our component directly.

The first prop that we pass into our component is the mapView - that comes directly from the parent component, index.tsx.

We will use the Experience Builder infrastructure to load any ArcGIS API for JavaScript modules we need to use, so this widget should mirror that. To do this, we use the loadModules module from esri-loader and load whatever we want and pass it into our component as the second prop, modules.

Our widget is going to operate on a Feature Layer, so the third prop is the string ID of the Feature Layer we're working with. In this example app we know it because we created the Feature Layer ourselves in Map.js, but in Experience Builder that will be configured in the Builder interface and will be passed into our component from the config.


One last step is to include the browserslist property in your package.json file. This fixes an error that happens because the app is trying to support older browsers.

All the changes for this step are summarized here.

Update the Component

Since we'll be using the ArcGIS API for JavaScript, first we setup our component to use the type definitions. First run:

npm install --saveDev @types/arcgis-js-api

Then add this as the first line to src/index.tsx:

/// <reference types="@types/arcgis-js-api" />

This tells the TypeScript compiler to use these types, and now you can use the variable __esri to refer to any types in the ArcGIS API for JavaScript.

Next, build out the new functionality for your widget. This is where your widget will deviate from our example. In our example, we're creating a Sketch widget and doing a query against the Feature Layer to find the number of intersecting features after each drawing. Full code is here.

Update the Experience Builder Widget

In part 1, we created a basic wrapper around the component, which did not take any props as input. In this part 2, the component will now take three props: the MapView, the modules, and the Feature Layer ID. We added these to the Example App above, and similarly they must be added to the Experience Builder widget.

Unlike the Example app above where we had to install and import esri-loader (to get access to the ArcGIS API for JavaScript) and create a map, in Experience Builder we already have those - you can simply drag the Map object into the Experience. We do have to configure the custom widget to know which map widget it should operate on, so that's what we'll do first.

Start up Experience Builder and setup your custom widget per the instructions in part 1.

Then, in your Experience, add a Map widget. You probably want to choose a web map that contains the layer you're using in the Example App, but not totally necessary.

Next, create a Settings panel to allow the Experience Author to choose the Map and LayerID. To do this, create src/settings/settings.tsx and other required files, and use the JimuMapViewSelector and the JimuLayerViewSelector. Full code is here.

completed settings panel

Next, update the widget code (src/runtime/widget.tsx) to pass in the props to the component. I'll go through each of the three props below.

1. MapView

When the user chooses a Map in the Settings panel that we created above, the config useMapWidgetIds gets set. To use this to get the MapView in the widget, use the JimuMapViewComponent to put the jimuMapView into state it can be used.

{this.props.hasOwnProperty("useMapWidgetIds") &&
  this.props.useMapWidgetIds &&
  this.props.useMapWidgetIds.length === 1 && (
      onActiveViewChange={(jmv: JimuMapView) => {
        if (jmv && jmv.view) {
            jimuMapView: jmv,

After that is set, we can update the component, passing in this state:

  mapView={this.state.jimuMapView ? this.state.jimuMapView.view : false}

2. featureLayerId

Similar to above, the Feature Layer ID gets set in the Settings. This one is simpler than JimuMapView though since it's just a string, all that is needed is to update the JSX:

  mapView={this.state.jimuMapView ? this.state.jimuMapView.view : false}

3. modules

To instruct Experience Builder to load the ArcGIS API for JavaScript for us, update the widget's manifest.json file to include a dependency property:

"dependency": ["jimu-arcgis"]

This allows the inclusion of modules directly in our widget like this:

const Sketch = require("esri/widgets/Sketch");

... and then just pass these imports as an array in the JSX, so our final component in our widget JSX will look like:

  mapView={this.state.jimuMapView ? this.state.jimuMapView.view : false}

(full code)

Completed Component and Widget

When complete, you can include your widget with an Experience to get something like this, all while still able to use your component in a React app like this.

The full, completed code for this tutorial can be found here (component) and here (Experience Builder widget wrapper). Thanks for reading, and use the comments for questions, discussion, and links to the widgets you've built.

2 0 846
Esri Contributor

We have received a few questions over the past few months about how to use "Custom ArcGIS API for JavaScript widgets" (like the Custom Recenter Widget) from within an Experience Builder widget. These are the current instructions on how to do that. There may be better or different ways - if you learn of one, please let us know!

Note: Last updated 2020-07 for ArcGIS JS API 4.16 and Experience Builder Developer Edition v1.1.

1. Build the JavaScript API Widget

Follow the instructions in the documentation for the ArcGIS API for JavaScript to Create a Custom Widget and then create the Custom Recenter Widget. These guides do not use webpack to package the files - they are only using the TypeScript compiler (tsc) to convert the *.tsx files to *.js files.

After you've run the tsc command to convert the tsx files to js files one last time, copy the JS file (Recenter.js) and move on - we will copy this into our custom Experience Builder widget in the next step.

2. Create a Custom Experience Builder widget

Create a new custom Experience Builder widget. The easiest way to get going quickly is probably to copy the simple widget.

Since the JS API Widget interacts with a Map View, your Experience Builder widget will need the infrastructure to reference a Map View widget within the same Experience app. The easiest way to do this is to create an Experience, add your widget, then go to the config for your working app (server\src\public\apps\Z\resources\config) and set "useMapWidgetIds": ["widget_ZZ"], in the widget config.

example config

In your widget manifest.json, include the jimu-arcgis dependency by adding:

"dependency": ["jimu-arcgis"],‍‍‍

Copy the JS file that you had from step 1 (Recenter.js) into your widget files, at the same level as widget.tsx (within src/runtime).

Within widget.tsx, import the file:

import Recenter = require("./Recenter");‍‍‍

Then utilize the Recenter widget within the callback of JimuMapViewComponent or anywhere you have an instance of the Map View:

render() {
  return (
    <div className="widget-demo jimu-widget m-2">
      {this.props.hasOwnProperty("useMapWidgetIds") &&
        this.props.useMapWidgetIds &&
        this.props.useMapWidgetIds.length === 1 && (
            onActiveViewChange={(jmv: JimuMapView) => {
              if (jmv) {
                jmv.view.when(function () {
                  const recenter = new Recenter({
                    view: jmv.view,
                    initialCenter: [-100.33, 43.69],

                  jmv.view.ui.add(recenter, "top-right");
      <p>Simple Widget</p>

For the above code to work, you'll need to import JimuMapViewComponent and JimuMapView at the top of your widget like this:

import { JimuMapViewComponent, JimuMapView } from "jimu-arcgis";

If successful, you'll see the info box in the top-right corner of the map when you add your widget to an Experience (and configure it manually to reference a map):

Next Steps

The next logical step would probably be to modify the widget to have the text inside the Experience Builder widget instead of on the map. I'll leave that up to you, and also please feel free to post other widgets and widget ideas you are building!

3 0 1,059
25 Subscribers