Select to view content in your preferred language

Map not able to plot large number of pins

771
4
Jump to solution
04-03-2022 08:32 PM
Tarak-Kar
New Contributor III

I am working on an Esri map-based app where I am plotting more than 1000 pins on the map. This works perfectly fine on the web app using ArcGIS JS SDK.

 

Now I am working on the ios version of the same app using iOS Runtime SDK. But on ios, if we try to load more than 100 pins on the map the memory surges to 2 GB and hits the upper limit of the available RAM on the iPad and hence crashes.

So is there any limitation?  Can you suggest a  better way to handle this?

Thanks 

 

Tags (2)
0 Kudos
1 Solution

Accepted Solutions
MarkDostal
Esri Contributor

Hello.  You can certainly use an AGSUniqueValueRenderer to accomplish what you want in a more performance-sensitive way.  Another way would be to optimize the "for" loop where you are creating the graphics.  Specifically:

- create the two images outside of the loop.  They don't need to be created each time

- the same for the Symbols.  Create the two symbols once, outside the "for" loop, then just use the appropriate symbol when creating the graphic

- use one of the other AGSPoint initializers, such as AGSPointMakeWGS84, to eliminate creating an intermediate object (this was covered in a separate question)

Finally, is there any way you could apply some optimizations to the "refreshData()" method?  You are removing all graphics and recreating them each time.  Is it possible to only remove/recreate a sub-set of them?  If only the geometries are changing, you could possible simply update the geometries for the graphics, instead of recreating everything?

Graphics have a "visible" property that allows you to hide/show them.  You can also set a new geometry or symbol on the graphic without recreating it.

Mark

View solution in original post

4 Replies
Nicholas-Furness
Esri Regular Contributor

Hi. The Runtime SDKs were built from the outset with performance in mind and should be able to handle 10s of thousands of pins with ease.

I don't know how you're approaching this to hit that memory spike, but one approach which should certainly work is to set up an AGSGraphicsOverlay on your AGSMapView (add it to the graphicsOverlays collection), and add 1000 AGSGraphics to the AGSGraphicsOverlay's graphics collection.

Each AGSGraphic can have its own symbol, but the most efficient way to handle this is to set up a renderer on the AGSGraphicsOverlay, which can efficiently share symbols between graphics in the overlay. If all your points will be displayed the same way, an AGSSimpleRenderer is suitable. Otherwise, if points should be displayed based off an attribute, use an AGSUniqueValueRenderer and populate the attributes dictionary on each AGSGraphic.

These samples may help:

One other thought: if you are using images for your symbols with an AGSPictureMarkerSymbol, be sure the image is a sensible size. You don't need a 10MB image just to display a point on a map. Consider scaling it appropriately.

Hope that helps.

Tarak-Kar
New Contributor III

Hi Nicholas, Thanks for your quick response.

I am using AGSPictureMarkerSymbol to show the different images on the map and the image size is around 5kb each. My use case is like, I want to plot thousands of AGSPictureMarkerSymbol and I want to replot (remove and add) those AGSPictureMarkerSymbol on refresh action. Here is my code to achieve the above scenario,

 

func addMarkerSymbols() {
      var i = 0
     for mapObj in filteredData {
     var dislayImage = mapObj.status == "COMP" ? #imageLiteral(resourceName: "unknown_comp") : #imageLiteral(resourceName: "unknown")
 
     let pinSymbol = AGSPictureMarkerSymbol(image: dislayImage)
 
     pinSymbol.width = 40
     pinSymbol.height = 40
 
     // change offsets, so the symbol aligns properly to the point
     pinSymbol.offsetY = pinSymbol.image!.size.height / 2
 
     // location for pin
     guard let latitude = mapObj.latitude, let longitude = mapObj.longitude else {
            i += 1
          continue
     }
     let loc = CLLocationCoordinate2DMake(latitude.doubleValue(), longitude.doubleValue())
     let pinPoint = AGSPoint(clLocationCoordinate2D: loc)
 
    // graphic for pin
    let graphic = AGSGraphic(geometry: pinPoint, symbol: pinSymbol, attributes: ["index": I])
 
    // add the graphic to the overlay
    self.graphicsOverlay.graphics.add(graphic)
     i += 1
    }
}
func refreshData() {
     self.graphicsOverlay.graphics.removeAllObjects()
     self.addMarkerSymbols()
}
Please suggest if there is any better way to achieve this.
 
Thanks.
 
0 Kudos
MarkDostal
Esri Contributor

Hello.  You can certainly use an AGSUniqueValueRenderer to accomplish what you want in a more performance-sensitive way.  Another way would be to optimize the "for" loop where you are creating the graphics.  Specifically:

- create the two images outside of the loop.  They don't need to be created each time

- the same for the Symbols.  Create the two symbols once, outside the "for" loop, then just use the appropriate symbol when creating the graphic

- use one of the other AGSPoint initializers, such as AGSPointMakeWGS84, to eliminate creating an intermediate object (this was covered in a separate question)

Finally, is there any way you could apply some optimizations to the "refreshData()" method?  You are removing all graphics and recreating them each time.  Is it possible to only remove/recreate a sub-set of them?  If only the geometries are changing, you could possible simply update the geometries for the graphics, instead of recreating everything?

Graphics have a "visible" property that allows you to hide/show them.  You can also set a new geometry or symbol on the graphic without recreating it.

Mark

Tarak-Kar
New Contributor III

Hello, thanks for the answer. Creating Symbols outside the for loop did the magic.

0 Kudos