Style graphics with renderer

View on GitHub

A renderer allows you to change the style of all graphics in a graphics overlay by referencing a single symbol style. A renderer will only affect graphics that do not specify their own symbol style.

Image of style graphics with renderer

Use case

A renderer allows you to change the style of all graphics in an overlay by only changing one copy of the symbol. For example, a user may wish to display a number of graphics on a map of parkland which represent trees, all sharing a common symbol.

How to use the sample

Pan and zoom on the map to view graphics for points, lines, and polygons (including polygons with curved segments), which are stylized using renderers.

How it works

  1. Create a GraphicsOverlay and add it to the ArcGISMapViewController.
  2. Create a Graphic, specifying only a Geometry.
  3. Create a single ArcGISSymbol such as a SimpleMarkerSymbol.
  4. Create a renderer with SimpleRenderer(), passing in an ArcGISSymbol.
  5. Set the renderer for the GraphicsOverlay.

Relevant API

  • CubicBezierSegment
  • EllipticArcSegment
  • GeodesicEllipseParameters
  • Geometry
  • GeometryEngine.ellipseGeodesic
  • Graphic
  • GraphicsOverlay
  • MutablePart
  • PolygonBuilder
  • PolylineBuilder
  • SimpleFillSymbol
  • SimpleLineSymbol
  • SimpleMarkerSymbol
  • SimpleRenderer

Tags

arc, bezier, curve, display, ellipse, graphics, marker, overlay, renderer, segment, symbol, true curve

Sample Code

style_graphics_with_renderer.dart
Use dark colors for code blocksCopy
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
// Copyright 2024 Esri
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import 'dart:math';

import 'package:arcgis_maps/arcgis_maps.dart';
import 'package:arcgis_maps_sdk_flutter_samples/utils/sample_state_support.dart';
import 'package:flutter/material.dart';

class StyleGraphicsWithRenderer extends StatefulWidget {
  const StyleGraphicsWithRenderer({super.key});

  @override
  State<StyleGraphicsWithRenderer> createState() =>
      _StyleGraphicsWithRendererState();
}

class _StyleGraphicsWithRendererState extends State<StyleGraphicsWithRenderer>
    with SampleStateSupport {
  // Create a controller for the map view.
  final _mapViewController = ArcGISMapView.createController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ArcGISMapView(
        controllerProvider: () => _mapViewController,
        onMapViewReady: onMapViewReady,
      ),
    );
  }

  void onMapViewReady() {
    // Create a map with a topographic basemap style.
    final map = ArcGISMap.withBasemapStyle(BasemapStyle.arcGISTopographic);

    // Add graphics overlays to the mapview controller.
    _mapViewController.graphicsOverlays.add(getPointGraphicsOverlay());
    _mapViewController.graphicsOverlays.add(getSquarePolygonGraphicsOverlay());
    _mapViewController.graphicsOverlays.add(getLineGraphicsOverlay());
    _mapViewController.graphicsOverlays.add(getEllipseGraphicsOverlay());
    _mapViewController.graphicsOverlays.add(getCurvedPolygonGraphicsOverlay());

    // Set the map to the map view controller.
    _mapViewController.arcGISMap = map;

    // Combined extent of all the graphic overlays.
    final combinedExtent = Envelope.fromXY(
      spatialReference: SpatialReference.webMercator,
      xMin: -3000000,
      yMin: -1000000,
      xMax: 6000000,
      yMax: 7000000,
    );
    // Set the viewpoint of the map view controller to the combined extent.
    _mapViewController.setViewpointGeometry(combinedExtent);
  }

  GraphicsOverlay getPointGraphicsOverlay() {
    // Create a simple marker symbol.
    final pointSymbol = SimpleMarkerSymbol(
      style: SimpleMarkerSymbolStyle.diamond,
      color: Colors.green,
      size: 10,
    );
    // Create a graphics overlay for the points.
    final pointGraphicsOverlay = GraphicsOverlay();
    // Create and assign a simple renderer to the graphics overlay.
    pointGraphicsOverlay.renderer = SimpleRenderer(symbol: pointSymbol);

    // Create a point graphic with a point geometry.
    final pointGeometry = ArcGISPoint(
      x: 40e5,
      y: 40e5,
      spatialReference: SpatialReference.webMercator,
    );
    final pointGraphic = Graphic(geometry: pointGeometry);
    // Add the graphic to the graphics overlay.
    pointGraphicsOverlay.graphics.add(pointGraphic);

    return pointGraphicsOverlay;
  }

  GraphicsOverlay getLineGraphicsOverlay() {
    // Create a simple line symbol.
    final lineSymbol = SimpleLineSymbol(
      color: Colors.blue,
      width: 5,
    );
    // Create a graphics overlay for the line.
    final lineGraphicsOverlay = GraphicsOverlay();
    // Create and assign a simple render to the graphics overlay.
    lineGraphicsOverlay.renderer = SimpleRenderer(symbol: lineSymbol);

    // Create a line with a polyline geometry.
    final lineBuilder =
        PolylineBuilder(spatialReference: SpatialReference.webMercator);
    lineBuilder.addPointXY(x: -10e5, y: 40e5);
    lineBuilder.addPointXY(x: 20e5, y: 50e5);
    final lineGraphic = Graphic(geometry: lineBuilder.toGeometry());
    // Add the graphic to the graphics overlay.
    lineGraphicsOverlay.graphics.add(lineGraphic);

    return lineGraphicsOverlay;
  }

  GraphicsOverlay getSquarePolygonGraphicsOverlay() {
    // Create a simple fill symbol.
    final squareSymbol = SimpleFillSymbol(color: Colors.yellow);
    // Create a graphics overlay for the square polygons.
    final squareGraphicsOverlay = GraphicsOverlay();
    // Create and assign a simple renderer to the graphics overlay.
    squareGraphicsOverlay.renderer = SimpleRenderer(symbol: squareSymbol);

    // Create a polygon graphic with `Polygon` geometry.
    final polygonBuilder =
        PolygonBuilder(spatialReference: SpatialReference.webMercator);
    polygonBuilder.addPoint(ArcGISPoint(x: -20e5, y: 20e5));
    polygonBuilder.addPoint(ArcGISPoint(x: 20e5, y: 20e5));
    polygonBuilder.addPoint(ArcGISPoint(x: 20e5, y: -20e5));
    polygonBuilder.addPoint(ArcGISPoint(x: -20e5, y: -20e5));
    final polygonGraphic = Graphic(geometry: polygonBuilder.toGeometry());
    // Add the graphic to the overlay.
    squareGraphicsOverlay.graphics.add(polygonGraphic);

    return squareGraphicsOverlay;
  }

  GraphicsOverlay getEllipseGraphicsOverlay() {
    // Create a simple fill symbol.
    final ellipseFillSymbol = SimpleFillSymbol(color: Colors.purple);
    // Create a graphics overlay for the ellipse.
    final ellipseGraphicsOverlay = GraphicsOverlay();
    // Create and assign a simple renderer for the ellipse.
    ellipseGraphicsOverlay.renderer = SimpleRenderer(symbol: ellipseFillSymbol);

    // Create an ellipse graphic.
    final ellipseCenter = ArcGISPoint(
      x: 40e5,
      y: 25e5,
      spatialReference: SpatialReference.webMercator,
    );
    final parameters = GeodesicEllipseParameters(
      axisDirection: -45,
      angularUnit: AngularUnit(unitId: AngularUnitId.degrees),
      center: ellipseCenter,
      linearUnit: LinearUnit(unitId: LinearUnitId.kilometers),
      maxPointCount: 100,
      maxSegmentLength: 20,
      geometryType: GeometryType.polygon,
      semiAxis1Length: 200,
      semiAxis2Length: 400,
    );
    final ellipseGeometry =
        GeometryEngine.ellipseGeodesic(parameters: parameters);
    final ellipseGraphic = Graphic(geometry: ellipseGeometry);

    // Add the graphic to the overlay.
    ellipseGraphicsOverlay.graphics.add(ellipseGraphic);
    return ellipseGraphicsOverlay;
  }

  GraphicsOverlay getCurvedPolygonGraphicsOverlay() {
    // Create a simple fill symbol with outline.
    final curvedLineSymbol = SimpleLineSymbol(color: Colors.black);
    final curvedFillSymbol = SimpleFillSymbol(
      color: Colors.red,
      outline: curvedLineSymbol,
    );
    // Create a graphics overlay for the polygons with curve segments.
    final curvedGraphicsOverlay = GraphicsOverlay();
    // Create and assign a simple renderer to the graphics overlay.
    curvedGraphicsOverlay.renderer = SimpleRenderer(symbol: curvedFillSymbol);

    // Create a heart shape graphic from a segment.
    final origin = ArcGISPoint(
      x: 40e5,
      y: 5e5,
      spatialReference: SpatialReference.webMercator,
    );
    final heartGeometry = getHeartGeometry(center: origin, sideLength: 10e5);
    final heartGraphic = Graphic(geometry: heartGeometry);

    // Add the graphic to the overlay.
    curvedGraphicsOverlay.graphics.add(heartGraphic);
    return curvedGraphicsOverlay;
  }

  Geometry getHeartGeometry({
    required ArcGISPoint center,
    required double sideLength,
  }) {
    // The side length should be always greater than 0.
    if (sideLength <= 0) {
      throw ArgumentError.value(sideLength, 'sideLength', 'Must be positive');
    }

    final spatialReference = center.spatialReference;
    // The x and y coordinates to simplify the calculation.
    final minX = center.x - 0.5 * sideLength;
    final minY = center.y - 0.5 * sideLength;
    // The radius of the arcs.
    final arcRadius = sideLength * 0.25;

    // Bottom left curve.
    final leftCurveStart =
        ArcGISPoint(x: center.x, y: minY, spatialReference: spatialReference);
    final leftCurveEnd = ArcGISPoint(
      x: minX,
      y: minY + 0.75 * sideLength,
      spatialReference: spatialReference,
    );
    final leftControlPoint1 = ArcGISPoint(
      x: center.x,
      y: minY + 0.25 * sideLength,
      spatialReference: spatialReference,
    );
    final leftControlPoint2 =
        ArcGISPoint(x: minX, y: center.y, spatialReference: spatialReference);
    final leftCurve = CubicBezierSegment(
      startPoint: leftCurveStart,
      controlPoint1: leftControlPoint1,
      controlPoint2: leftControlPoint2,
      endPoint: leftCurveEnd,
      spatialReference: spatialReference,
    );

    // Top left arc.
    final leftArcCenter = ArcGISPoint(
      x: minX + 0.25 * sideLength,
      y: minY + 0.75 * sideLength,
      spatialReference: spatialReference,
    );
    final leftArc =
        EllipticArcSegment.circularEllipticArcWithCenterRadiusAndAngles(
      centerPoint: leftArcCenter,
      radius: arcRadius,
      startAngle: pi,
      centralAngle: -pi,
      spatialReference: spatialReference,
    );

    // Top right arc.
    final rightArcCenter = ArcGISPoint(
      x: minX + 0.75 * sideLength,
      y: minY + 0.75 * sideLength,
      spatialReference: spatialReference,
    );
    final rightArc =
        EllipticArcSegment.circularEllipticArcWithCenterRadiusAndAngles(
      centerPoint: rightArcCenter,
      radius: arcRadius,
      startAngle: pi,
      centralAngle: -pi,
      spatialReference: spatialReference,
    );

    // Bottom right curve.
    final rightCurveStart = ArcGISPoint(
      x: minX + sideLength,
      y: minY + 0.75 * sideLength,
      spatialReference: spatialReference,
    );
    final rightCurveEnd = leftCurveStart;
    final rightControlPoint1 = ArcGISPoint(
      x: minX + sideLength,
      y: center.y,
      spatialReference: spatialReference,
    );
    final rightControlPoint2 = leftControlPoint1;
    final rightCurve = CubicBezierSegment(
      startPoint: rightCurveStart,
      controlPoint1: rightControlPoint1,
      controlPoint2: rightControlPoint2,
      endPoint: rightCurveEnd,
      spatialReference: spatialReference,
    );

    final heart = MutablePart(spatialReference: spatialReference);
    for (final segment in [leftCurve, leftArc, rightArc, rightCurve]) {
      heart.addSegment(segment);
    }

    final heartShape = PolygonBuilder(spatialReference: spatialReference);
    heartShape.parts.addPart(heart);
    return heartShape.toGeometry();
  }
}

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