Select to view content in your preferred language

AGSRenderer subclass

3725
6
Jump to solution
03-14-2013 06:02 AM
PatrickHartling
Occasional Contributor
I have an AGSGraphicsLayer that currently uses AGSClassBreaksRenderer. All data has point geometry, and I am using AGSSimpleMarkerSymbol for each class break. This is quite nice when zoomed in sufficiently close, but I would like to be able to have another renderer that can visualize the graphics as a continuous gradient when the markers would overlap and lose distinction.

I expected to be able to make a subclass of AGSRenderer, but it is not clear to me how AGSGraphicsLayer and AGSRenderer interact to perform the rendering. Does the layer ask its renderer for a visual representation of each AGSGraphic? Does the visual representation of a graphic always end up being a swatch/image?
0 Kudos
1 Solution

Accepted Solutions
DanaMaher
Regular Contributor

The wrinkle here is that I am receiving the data from an external source as it is read from various sensors. It is not coming from ArcGIS Server or from any mapping service. It sounds as though I need a custom subclass of AGSDynamicLayer that renders the density representation one way or another. Is that reasonable?


Interesting. To my knowledge, the various ArcGIS Runtime SDKs leave rendering of actual map tiles to backing map services. Only discreet geometries/graphics are rendered client-side. So, if not for your custom service, it would seem like the AGSFeatureLayer / AGSDynamicMapServiceLayer switching would be the way to go.

In the 2.3 SDK, where layers were rendered within a hierarchy of UIViews inside AGSMapView, I have a hunch that you could:

  1. Subclass AGSDynamicLayer

  2. Override the drawing code for the UILayer associated with the UIView that represents the AGSDynamicLayer in the AGSMapView.

  3. Write your own tile image generator (a lot of work!) to perform the drawing in the overridden UILayer from 2).

This would involve use of undocumented API's and would probably be difficult to perfect since the AGSMapView does a lot of resizing of its internal components that you are not able to access or override. You would have to basically work with the private methods/components you figure out and around the ones you don't know and/or cannot override. It might be functional, but it will probably be a messy hack behind the scenes.

Unfortunately, with the switch to OpenGLES rendering in 10.1.1, access to the individual components of an AGSMapView is no longer possible; everything is being rendered into an OpenGLES window. The ESRI devs will obviously know more than I do, but I do have some experience with the SDK.

My two cents is that the subclass you are thinking of will have to work against some of the basic conventions/architectural choices of the SDK and that you want to either:

  1. Figure out a clustering algorithm that will allow you to represent graphics in aggregate at larger scales. This does not get you a true density map but will allow the user to view clusters of graphics at larger scales.

  2. Figure out a way to feed your external map service into an ESRI service. Assuming, of course, that you have the license to set up and use such a service.

View solution in original post

0 Kudos
6 Replies
DanaMaher
Regular Contributor
... Does the layer ask its renderer for a visual representation of each AGSGraphic? Does the visual representation of a graphic always end up being a swatch/image?


Yes, and yes. AGSGraphic and AGSGraphicsLayer are designed around representing and interacting with discrete objects/geometries. If I understand your desired workflow correctly, you want to switch from discrete graphics to a density representation at a certain scale. You should be able to accomplish this by having two services; one feature service that returns the discrete graphics and one dynamic or tiled service that returns the density map. You can switch between displaying one or the other once you cross your scale threshold.

You might also explore clustering algorithms. I've poked around with writing a k-means clustering kit for the ArcGIS Runtime SDK. This clustering library also has a good example of a more basic algorithm that could be ported over fairly easily.
0 Kudos
PatrickHartling
Occasional Contributor
If I understand your desired workflow correctly, you want to switch from discrete graphics to a density representation at a certain scale.


Correct.

You should be able to accomplish this by having two services; one feature service that returns the discrete graphics and one dynamic or tiled service that returns the density map. You can switch between displaying one or the other once you cross your scale threshold.


The wrinkle here is that I am receiving the data from an external source as it is read from various sensors. It is not coming from ArcGIS Server or from any mapping service. It sounds as though I need a custom subclass of AGSDynamicLayer that renders the density representation one way or another. Is that reasonable?

You might also explore clustering algorithms. I've poked around with writing a k-means clustering kit for the ArcGIS Runtime SDK. This clustering library also has a good example of a more basic algorithm that could be ported over fairly easily.


Thanks for the tip. I am looking into it.
0 Kudos
DanaMaher
Regular Contributor

The wrinkle here is that I am receiving the data from an external source as it is read from various sensors. It is not coming from ArcGIS Server or from any mapping service. It sounds as though I need a custom subclass of AGSDynamicLayer that renders the density representation one way or another. Is that reasonable?


Interesting. To my knowledge, the various ArcGIS Runtime SDKs leave rendering of actual map tiles to backing map services. Only discreet geometries/graphics are rendered client-side. So, if not for your custom service, it would seem like the AGSFeatureLayer / AGSDynamicMapServiceLayer switching would be the way to go.

In the 2.3 SDK, where layers were rendered within a hierarchy of UIViews inside AGSMapView, I have a hunch that you could:

  1. Subclass AGSDynamicLayer

  2. Override the drawing code for the UILayer associated with the UIView that represents the AGSDynamicLayer in the AGSMapView.

  3. Write your own tile image generator (a lot of work!) to perform the drawing in the overridden UILayer from 2).

This would involve use of undocumented API's and would probably be difficult to perfect since the AGSMapView does a lot of resizing of its internal components that you are not able to access or override. You would have to basically work with the private methods/components you figure out and around the ones you don't know and/or cannot override. It might be functional, but it will probably be a messy hack behind the scenes.

Unfortunately, with the switch to OpenGLES rendering in 10.1.1, access to the individual components of an AGSMapView is no longer possible; everything is being rendered into an OpenGLES window. The ESRI devs will obviously know more than I do, but I do have some experience with the SDK.

My two cents is that the subclass you are thinking of will have to work against some of the basic conventions/architectural choices of the SDK and that you want to either:

  1. Figure out a clustering algorithm that will allow you to represent graphics in aggregate at larger scales. This does not get you a true density map but will allow the user to view clusters of graphics at larger scales.

  2. Figure out a way to feed your external map service into an ESRI service. Assuming, of course, that you have the license to set up and use such a service.

0 Kudos
PatrickHartling
Occasional Contributor
My two cents is that the subclass you are thinking of will have to work against some of the basic conventions/architectural choices of the SDK and that you want to either:

  1. Figure out a clustering algorithm that will allow you to represent graphics in aggregate at larger scales. This does not get you a true density map but will allow the user to view clusters of graphics at larger scales.

  2. Figure out a way to feed your external map service into an ESRI service. Assuming, of course, that you have the license to set up and use such a service.



What I have tried thus far does seem to be going against the grain. My proof of concept uses an instance of CALayer that I create and add as a sub-layer of the CAEAGLLayer used by AGSMapView. The behavior is quite poor due to the high latency on state change notifications from the AGSMapView object. My custom layer always lags way behind during panning and zooming. My real goal is to use an OpenGL texture to provide the visualization, but that would still have the same zooming and panning problems that I have with my trivial CALayer usage. I get the feeling that some sort of low level drawing hook into the CAEAGLLayer of the map view is what I would need to get the best results, but I do not see a way to make that happen.
0 Kudos
DanaMaher
Regular Contributor
What I have tried thus far does seem to be going against the grain. My proof of concept uses an instance of CALayer that I create and add as a sub-layer of the CAEAGLLayer used by AGSMapView. The behavior is quite poor due to the high latency on state change notifications from the AGSMapView object. My custom layer always lags way behind during panning and zooming. My real goal is to use an OpenGL texture to provide the visualization, but that would still have the same zooming and panning problems that I have with my trivial CALayer usage. I get the feeling that some sort of low level drawing hook into the CAEAGLLayer of the map view is what I would need to get the best results, but I do not see a way to make that happen.


Yeah, that is the same limitation I ran into playing with the 2.3 SDK. I had some luck making the custom layer (UIView in my case) transparent between an extent changing user gesture and the eventual mapViewDid_____ notification. However, this looked pretty awful; it's been years since your average web/mobile map had to suspend drawing while changing extent and there was sometimes still a moment of the render for the previous extent showing up.
0 Kudos
PatrickHartling
Occasional Contributor
I think I am pretty close to having this resolved. I had not looked closely enough at AGSLayer(ForSubclassEyesOnly) and AGSDynamicLayer(ForSubclassEyesOnly). The rendering hook that I need is in the form of two methods in the latter category: -requestImageWithWidth:height:envelope:timeExtent: and -setImageData:forEnvelope:. The first will be sent to my AGSDynamicLayer subclass after I send -layerDidLoad to it. When there is new data to render, I send -refresh to my AGSDynamicLayer subclass, and the request method is invoked again.

I am now at a point where I am focused on developing the algorithm that produces the image data that is the request response. Using CALayer was definitely never going to get me to that point. Thanks for the pointers and the feedback.
0 Kudos