Rendering performance: FeatureLayer vs GraphicsLayer

1692
0
04-30-2021 10:13 PM
Status: Open
Labels (3)
nkiabee
New Contributor II

Back in those days when we were developing our modules on ArcMap, we were advised by esri developers to use feature layer rather than graphics layer as it has better performance. However, we noticed that the rendering performance for objects in FeatureLayer is worst than GraphicsLayer when switching our development into ArcGIS API for Javascript .

I attached a sample code below, where feature and graphic objects are drawn in blue and red color respectively. You will notice that when fast zoom (use mouse scroll) in/out of the view, red objects always render faster than blue objects.

Another point to note, we also noticed that the rendering performance gets worst as we add more FeatureLayers to the view, even when there are not feature in these layers (The sample codes can show that too).

We like to believe that the architecture/concept for FeatureLayer should still be better than GraphicsLayer in terms of rendering performance, and the current issue is due to some design/implementation of the underlying the API libraries that can be rectified.

 

Sample Code:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>

<title>
Create a FeatureLayer | Sample | ArcGIS API for JavaScript 4.19
</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>

<link
rel="stylesheet"
href="https://js.arcgis.com/4.19/esri/themes/light/main.css"
/>

<script src="https://js.arcgis.com/4.19/"></script>

<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/layers/FeatureLayer",
"esri/layers/GraphicsLayer",
"esri/core/promiseUtils",
"esri/geometry/Polygon",
"esri/Graphic"
], function (Map, MapView, FeatureLayer, GraphicsLayer, promiseUtils, Polygon, Graphic) {
const view = new MapView({
map: new Map({
basemap: "gray-vector"
}),

container: "viewDiv",
extent: {
spatialReference: {
wkid: 4326
},

xmin: 102,
ymin: 1,
xmax: 105,
ymax: 3
}
});

view
.when()
.then(createLayers)
.then(addToView)
.catch(function (e) {
console.error("Creating FeatureLayer failed", e);
});

// Create a symbol for rendering the graphic
var fillSymbol = {
type: "simple-fill", // autocasts as new SimpleFillSymbol()
style: "none",
outline: {
// autocasts as new SimpleLineSymbol()
color: [255, 0, 0],
width: 2
}
};

function toRadians(angleInDegrees) {
return (angleInDegrees * Math.PI) / 180;
}

function toDegrees(angleInRadians) {
return (angleInRadians * 180) / Math.PI;
}

function offset(c1, distance, earthRadius, bearing) {
var lat1 = toRadians(c1[1]);
var lon1 = toRadians(c1[0]);
var dByR = distance / earthRadius;
var lat = Math.asin(
Math.sin(lat1) * Math.cos(dByR) +
Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing)
);
var lon =
lon1 +
Math.atan2(
Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat)
);
return [toDegrees(lon), toDegrees(lat)];
}

function circleToPolygon(center, radius, options) {
var n = 32;
var earthRadius = 6378137;

var coordinates = [];
for (var i = 0; i < n; ++i) {
coordinates.push(
offset(center, radius, earthRadius, (2 * Math.PI * -i) / n)
);
}

coordinates.push(coordinates[0]);

return {
type: "Polygon",
coordinates: [coordinates]
};
}

function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}

function createLayers() {
var fields = [
{
name: "ObjectID",
alias: "ObjectID",
type: "oid"
},
{
name: "areaName",
alias: "areaName",
type: "string"
},
{
name: "areaCode",
alias: "areaCode",
type: "string"
}
];

var layerRenderer = {
type: "simple",
symbol: {
type: "simple-fill",//"simple-line",
style: "none",
outline: {
// autocasts as new SimpleLineSymbol()
color: [12, 60, 142, 1],
width: 2
}
}
};

// create x number of featurelayers
var layers = [];
for (var l = 0; l < 1; l++) {
// Create polygons for a featurelayer
var features = [];
for (var i = 0; i < 500; i++) {
var randx = getRandomArbitrary(103.7, 103.75);
var randy = getRandomArbitrary(1.3, 1.35);
var polygon = circleToPolygon([randx, randy], 500, 100);
var xx = {
geometry: {
type: "polygon",
rings: polygon.coordinates
},
attributes: { ObjectID: i }
};
features.push(xx);
}

// create a featurelayer
var featureLayer = new FeatureLayer({
source: features,
fields: fields,
objectIdField: "ObjectID",
renderer: layerRenderer,
spatialReference: {
wkid: 4326
},

outFields: ["*"],
geometryType: "polygon"
});

layers.push(featureLayer);

layerRenderer.symbol.outline.color = [
Math.floor(Math.random() * 254),
Math.floor(Math.random() * 254),
Math.floor(Math.random() * 254)
];
}

// create x number of graphicslayers
for (var l = 0; l < 1; l++) {
// Create polygons for a graphicslayer
var graphics1 = [];
for (var i = 0; i < 500; i++) {
var randx = getRandomArbitrary(103.7, 103.75);
var randy = getRandomArbitrary(1.3, 1.35);
var polygon = circleToPolygon([randx, randy], 500, 100);
var xx = {
geometry: {
type: "polygon",
rings: polygon.coordinates
},
symbol: {
type: "simple-fill", // autocasts as new SimpleFillSymbol()
style: "none",
outline: {
// autocasts as new SimpleLineSymbol()
color: [255, 0, 0],
width: 2
}
}
}
graphics1.push(xx);
}

// create a graphicslayer
var graphicsLayer = new GraphicsLayer({
graphics: graphics1,
});

layers.push(graphicsLayer);
}

// add additional empty featurelayers
for (var a=0; a<10; a++) {
var featureLayer1 = new FeatureLayer({
source: [],
fields: fields,
objectIdField: "ObjectID",
renderer: layerRenderer,
spatialReference: {
wkid: 4326
},

outFields: ["*"],
geometryType: "polygon"
});
layers.push(featureLayer1);
}
return layers;
}

// Adds a given layer to the map in the view
function addToView(layers) {
for (var i = 0; i < layers.length; i++) {
view.map.add(layers[i]);
}
}
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>