MapImageLayer - dynamic data layer with table join

This sample demonstrates how to add a sublayer from a joined table data source in a MapImageLayer. This is useful for scenarios when you want to access and visualize data from a non-geographic table containing records that correspond to geometries in a map service sublayer.

The table must exist in a registered dynamic workspace in your Map Server. In this sample, we create a MapImageLayer from a map service containing census data in various jurisdictions. An"ancestry" table was published in a registered workspace, which contains the total number of people in each U.S. state who can trace ancestry to a number of different origins. That data is made available to the view by creating a dynamic layer with a table join. To join the table to the states map service layer you must do the following.

First, construct the MapImageLayer with one sublayer. Since the sublayer will be dynamically created from data not visible in the services directory we need to specify the source of the data. In this case it is a joined table.

1
2
3
4
5
6
source: {
  type: "data-layer",
  dataSource: {
    type: "join-table"
  }
}

Then we need to define the sources of the join: a left table and a right table. Typically the left table will be the table containing the geometries. In this case, it is a map service layer viewable in the services directory. Since it has a layer ID of 3, we need to indicate that in the object for the leftTableSource.

1
2
3
4
5
6
7
8
9
10
source: {
  type: "data-layer",
  dataSource: {
    type: "join-table",
    leftTableSource: {
      type: "map-layer",
      mapLayerId: 3
    }
  }
}

The rightTableSource is the table containing ancestry numbers for each state. Since this isn't a map service layer, we should indicate it as a data layer with a table data source since it is a single table. The table data source must indicate the workspace ID where the table is located and the table's name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
source: {
  type: "data-layer",
  dataSource: {
    type: "join-table",
    leftTableSource: {
      type: "map-layer",
      mapLayerId: 3
    },
    rightTableSource: {
      type: "data-layer",
      dataSource: {
        type: "table",
        workspaceId: "CensusFileGDBWorkspaceID",
        dataSourceName: "ancestry"
      }
    }
  }
}

Because the rightTableSource is another data layer, you can create a nested table join by setting thedataSource of that object to be another table join.

To complete this join operation, however, you must provide the primary key, or field name, from the left table and the foreign key, or field name, from the right table. This matches the records from the non-geographic ancestry table to the map service layer containing geometries.

Finally, you must specify the type of the join. In this case, it is a left outer join so we can retain records that don't match in the map service layer. The final source object should look like the following in the context of the MapImageLayer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const layer = new MapImageLayer({
  url: "https://sampleserver6.arcgisonline.com/arcgis/rest/services/Census/MapServer",
  sublayers: [
    {
      // This ID does not relate to a service layer ID. It can be
      // anything you want it to be.
      id: 0,
      renderer: renderer,
      opacity: 0.75,
      source: {
        type: "data-layer",
        dataSource: {
          type: "join-table",
          leftTableSource: {
            type: "map-layer",
            mapLayerId: 3
          },
          rightTableSource: {
            type: "data-layer",
            dataSource: {
              type: "table",
              workspaceId: "CensusFileGDBWorkspaceID",
              dataSourceName: "ancestry"
            }
          },
          leftTableKey: "STATE_NAME",
          rightTableKey: "State",
          joinType: "left-outer-join"
        }
      }
    }
  ]
});

When setting renderers or popups on the layer, you must prefix field values by the origin table name. Since we're visualizing the percentage of the population with Norwegian ancestry, we would reference fields in the following manner in the context of the renderer:

1
2
3
4
5
6
7
const renderer = {
  type: "class-breaks",  // autocasts as new ClassBreaksRenderer()
  field: "ancestry.Norwegian",
  normalizationField: "states.POP2007",
  normalizationType: "field",
  classBreakInfos: [ ... ]
};

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.

The developer dashboard has moved

You can no longer sign into this site. Go to your ArcGIS portal or the ArcGIS Location Platform dashboard to perform management tasks.

Your ArcGIS portal

Create, manage, and access API keys and OAuth 2.0 developer credentials, hosted layers, and data services.

Your ArcGIS Location Platform dashboard

Manage billing, monitor service usage, and access additional resources.

Learn more about these changes in the What's new in Esri Developers June 2024 blog post.

Close