Popups

Download Sample Application

Popups display attribute and media information for individual features on a map. This sample shows you how to display the popup information that a webmap has been configured with using the popups container view controller. The snippet below shows that when you have clicked on the MapView the pop-ups are fetched from the webmap. Once returned a popover is then displayed on the map.

//This code snippet shows you how to obtain and open a popup.

@property (strong) AGSWebMap *webMap;
@property (strong) IBOutlet NSPopover *popover;

//click at a point on the map and fetch the popups from the webmap at that point
-(void)mapView:(AGSMapView *)mapView didClickAtPoint:(CGPoint)screen mapPoint:(AGSPoint *)mappoint graphics:(NSDictionary *)graphics {
  ...
	[self.webMap fetchPopupsForExtent:[self.mapView toMapEnvelope:self.searchRect]];
}

//when the popups have bene retrieved then display them in a popup over
- (void)webMap:(AGSWebMap *)webMap didFinishFetchingPopupsForExtent:(AGSEnvelope *)extent {
    ...
		[self.popover showRelativeToRect:.....];
}

Sample Code

//SWIFT SAMPLE CODE
/*
Copyright 2014 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

http://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 Cocoa
import ArcGIS

let kWebMapItemID = "1966ef409a344d089b001df85332608f"

class PopupsSwiftSample: NSViewController, AGSMapViewTouchDelegate, AGSWebMapDelegate {

    @IBOutlet weak var mapView:AGSMapView!
    @IBOutlet weak var popoverVC:NSViewController!
    @IBOutlet weak var popover:NSPopover!
    var webMap:AGSWebMap!
    var popupsContainerVC:AGSPopupsContainerViewController!
    
    var mapPoint:AGSPoint!
    var searchRect:CGRect!
    
    //MARK: - awakeFromNib
    
    override func awakeFromNib() {
        
        //set mapView's touch delegate
        self.mapView.touchDelegate = self
        
        //Create a web map and Open it into map view
        self.webMap = AGSWebMap(itemId: kWebMapItemID, credential: nil)
        self.webMap.delegate = self
        self.webMap.zoomToDefaultExtentOnOpen = true
        
        //regiester to AGSMapViewDidEndZoomingNotification so we can close popover if it is visible
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "respondToZoom:", name: AGSMapViewDidEndZoomingNotification, object: self.mapView)
    }
    
    //MARK: - WebMap Delegate
    
    // -------------------------------------------------------------------------------
    //  webMapDidLoad
    // -------------------------------------------------------------------------------
    func webMapDidLoad(webMap: AGSWebMap!) {
        //open webmap in mapview
        self.webMap.openIntoMapView(self.mapView)
        //zoom to predefined extend with known spatial reference of the map
        let sr = AGSSpatialReference.webMercatorSpatialReference()
        let env = AGSEnvelope(xmin: -13044000, ymin:3855000, xmax:-13040000, ymax:3858000, spatialReference:sr)
        self.mapView.zoomToEnvelope(env, animated: true)
    }
    
    // -------------------------------------------------------------------------------
    //  webMap:didFailToLoadWithError
    // -------------------------------------------------------------------------------
    func webMap(webMap: AGSWebMap!, didFailToLoadWithError error: NSError!) {
        self.showError(error, withMessageText:"Failed to load webmap")
    }
    
    // -------------------------------------------------------------------------------
    //  webMap:didLoadLayer
    // -------------------------------------------------------------------------------
    func webMap(webMap: AGSWebMap!, didLoadLayer layer: AGSLayer!) {
        println("Layer \(layer.name) loaded..")
    }
    
    // -------------------------------------------------------------------------------
    //  webMap:didFailToLoadLayer:baseLayer:federated:withError
    // -------------------------------------------------------------------------------
    func webMap(webMap: AGSWebMap!, didFailToLoadLayer layerInfo: AGSWebMapLayerInfo!, baseLayer: Bool, federated: Bool, withError error: NSError!) {
        self.showError(error, withMessageText:"Failed to load layer")
        self.webMap.continueOpenAndSkipCurrentLayer()
    }
    
    // -------------------------------------------------------------------------------
    //  didOpenWebMap:intoMapView
    // -------------------------------------------------------------------------------
    func didOpenWebMap(webMap: AGSWebMap!, intoMapView mapView: AGSMapView!) {
        println("Webmap opened")
    }
    
    // -------------------------------------------------------------------------------
    //  webMap:didFetchPopups
    //
    //  this may be called multiple times, once for each layer whose popups are fetched
    //  the will not be called when there are no popups for any visible layers
    // -------------------------------------------------------------------------------
    func webMap(webMap: AGSWebMap!, didFetchPopups popups: [AnyObject]!, forExtent extent: AGSEnvelope!) {
        if self.popupsContainerVC == nil || self.popupsContainerVC.popups.count == 0 {
            //initialize the popups container view controller with first layer's popups
            self.popupsContainerVC = AGSPopupsContainerViewController(popups: popups)
        }
        else {
            //popups container view controller has already been initialized
            //hence add additional popups from other layers
            self.popupsContainerVC.showAdditionalPopups(popups)
        }
    }
    
    // -------------------------------------------------------------------------------
    //  webMap:didFinishFetchingPopupsForExtent
    //
    //  this will be called finally when the popups for all visible layers have been fetched
    // -------------------------------------------------------------------------------
    func webMap(webMap: AGSWebMap!, didFinishFetchingPopupsForExtent extent: AGSEnvelope!) {
    
        if self.popupsContainerVC != nil && self.popupsContainerVC.popups.count > 0 { //Found one or more popups
        
            //we want to display the popups container view controller inside a popover
            //set the popover's bounds to be the frame of popups container view controller's view frame
            //this enables the popups to load inside the popover's view
            self.popupsContainerVC.view.frame = self.popoverVC.view.bounds
            self.popupsContainerVC.view.autoresizingMask = .ViewWidthSizable | .ViewHeightSizable
            
            //add the popups container view controller's view to be the subview of popover
            self.popoverVC.view.addSubview(self.popupsContainerVC.view)
            
            //show popover
            self.popover.showRelativeToRect(self.searchRect, ofView:self.mapView, preferredEdge:NSMaxYEdge)
        }
        else { //there are no popups returned.
        
            //close existing popover
            self.popover.close()
            
            //show callout with a message
            self.mapView.callout.hidden = false
            self.mapView.callout.title = String(format: "X: %.f Y: %.f", self.mapPoint.x, self.mapPoint.y)
            self.mapView.callout.detail = "No Popups at this location!"
            self.mapView.callout.showCalloutAt(self.mapPoint, screenOffset:CGPointMake(0, 0), animated:true)
        }
    }
    
    
    //MARK: - MapView Touch Delegate
    
    // -------------------------------------------------------------------------------
    //  mapView:didClickAtPoint:mapPoint:graphics
    // -------------------------------------------------------------------------------
    func mapView(mapView: AGSMapView!, didClickAtPoint screen: CGPoint, mapPoint mappoint: AGSPoint!, features: [NSObject : AnyObject]!) {
    
        //set the mapPoint
        self.mapPoint = mappoint
        
        //hide the callout
        self.mapView.callout.hidden = true
        
        //clear all popups
        if self.popupsContainerVC != nil {
            self.popupsContainerVC.clearAllPopups()
        }
        
        //reset the subviews of popover so that the popups container view controller's view is removed
        //and the popover is prepared to display the new set of popups.
        if self.popoverVC.view.subviews.count > 0 {
            self.popoverVC.view.subviews = Array()
        }
        //create a bouding rectangle for the mapPoint with a small buffer, and fetch popups for this envelope.
        let buffer:CGFloat = 5
        self.searchRect = CGRectMake(screen.x - buffer,screen.y - buffer,buffer*2,buffer*2)
        self.webMap.fetchPopupsForExtent(self.mapView.toMapEnvelope(self.searchRect))
    }
    
    
    //MARK: - Show Error
    
    // -------------------------------------------------------------------------------
    //  showError:withMessageText
    // -------------------------------------------------------------------------------
    func showError(error:NSError, withMessageText messageText:String) {
        if let viewWindow = self.view.window {
            let alert = NSAlert()
            alert.messageText = messageText
            alert.informativeText = error.localizedDescription
            alert.beginSheetModalForWindow(self.view.window!, modalDelegate: self, didEndSelector: nil, contextInfo: nil)
        }
    }
    
    //MARK: - Respond to Zoom
    // -------------------------------------------------------------------------------
    //  respondToZoom:aNotification
    // -------------------------------------------------------------------------------
    func respondToZoom(aNotification:NSNotification) {
        //if close popover if visible
        if self.popover.shown {
            self.popover.close()
        }
    }
    
    //MARK: - deinit
    
    // -------------------------------------------------------------------------------
    //  deinit
    // -------------------------------------------------------------------------------
    deinit {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}
//OBJECTIVE C SAMPLE CODE
/*
 Copyright 2013 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
 
 http://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 "PopupsSample.h"

#define kWebMapItemID @"1966ef409a344d089b001df85332608f"

@interface PopupsSample ()

@property (strong) AGSPoint *mapPoint;
@property (assign) CGRect searchRect;

@end

@implementation PopupsSample

#pragma mark - awakeFromNib

// -------------------------------------------------------------------------------
//  awakeFromNib
// -------------------------------------------------------------------------------
- (void)awakeFromNib {
    
    //set mapView's touch delegate
    self.mapView.touchDelegate = self;
	
    //Create a web map and Open it into map view
	self.webMap = [AGSWebMap webMapWithItemId:kWebMapItemID credential:nil];
	self.webMap.delegate = self;
    self.webMap.zoomToDefaultExtentOnOpen = YES;
    
    //regiester to AGSMapViewDidEndZoomingNotification so we can close popover if it is visible
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(respondToZoom:) name:AGSMapViewDidEndZoomingNotification object:self.mapView];
}

#pragma mark - WebMap Delegate

// -------------------------------------------------------------------------------
//  webMapDidLoad
// -------------------------------------------------------------------------------
- (void)webMapDidLoad:(AGSWebMap *)webMap {
	//open webmap in mapview
	[self.webMap openIntoMapView:self.mapView];
    //zoom to predefined extend with known spatial reference of the map
	AGSSpatialReference *sr = [AGSSpatialReference webMercatorSpatialReference];
    AGSEnvelope *env = [AGSEnvelope envelopeWithXmin:-13044000 ymin:3855000 xmax:-13040000 ymax:3858000 spatialReference:sr];
	[self.mapView zoomToEnvelope:env animated:YES];
}

// -------------------------------------------------------------------------------
//  webMap:didFailToLoadWithError
// -------------------------------------------------------------------------------
- (void)webMap:(AGSWebMap *)webMap didFailToLoadWithError:(NSError *)error {
    [self showError:error withMessageText:@"Failed to load webmap"];
}

// -------------------------------------------------------------------------------
//  webMap:didLoadLayer
// -------------------------------------------------------------------------------
- (void)webMap:(AGSWebMap *)webMap didLoadLayer:(AGSLayer *)layer {
    NSLog(@"Layer %@ loaded..",layer.name);
}

// -------------------------------------------------------------------------------
//  webMap:didFailToLoadLayer:baseLayer:federated:withError
// -------------------------------------------------------------------------------
-(void)webMap:(AGSWebMap *)webMap didFailToLoadLayer:(AGSWebMapLayerInfo *)layerInfo baseLayer:(BOOL)baseLayer federated:(BOOL)federated withError:(NSError *)error {
    [self showError:error withMessageText:@"Failed to load layer"];
    [self.webMap continueOpenAndSkipCurrentLayer];
}

// -------------------------------------------------------------------------------
//  didOpenWebMap:intoMapView
// -------------------------------------------------------------------------------
-(void)didOpenWebMap:(AGSWebMap*)webMap intoMapView:(AGSMapView*)mapView {
    NSLog(@"Webmap opened");
}

// -------------------------------------------------------------------------------
//  didFailToOpenWebMap:intoMapView
// -------------------------------------------------------------------------------
-(void)didFailToOpenWebMap:(AGSWebMap*)webMap intoMapView:(AGSMapView*)mapView {
    [self showError:nil withMessageText:@"Failed to open webmap"];
}

// -------------------------------------------------------------------------------
//  webMap:didFetchPopups
//
//  this may be called multiple times, once for each layer whose popups are fetched
//  the will not be called when there are no popups for any visible layers
// -------------------------------------------------------------------------------
- (void)webMap:(AGSWebMap *)webMap didFetchPopups:(NSArray *)popups forExtent:(AGSEnvelope *)extent {
    
	if(!self.popupsContainerVC || ([self.popupsContainerVC.popups count] == 0)) {
        //initialize the popups container view controller with first layer's popups
		self.popupsContainerVC = [[AGSPopupsContainerViewController alloc] initWithPopups:popups];
	}
	else {
        //popups container view controller has already been initialized
        //hence add additional popups from other layers
		[self.popupsContainerVC showAdditionalPopups:popups];
	}
}

// -------------------------------------------------------------------------------
//  webMap:didFinishFetchingPopupsForExtent
//
//  this will be called finally when the popups for all visible layers have been fetched
// -------------------------------------------------------------------------------
- (void)webMap:(AGSWebMap *)webMap didFinishFetchingPopupsForExtent:(AGSEnvelope *)extent {
    
	if ([self.popupsContainerVC.popups count] > 0) { //Found one or more popups
        
        //we want to display the popups container view controller inside a popover
        //set the popover's bounds to be the frame of popups container view controller's view frame
        //this enables the popups to load inside the popover's view
		self.popupsContainerVC.view.frame = [self.popoverVC.view bounds];
		[self.popupsContainerVC.view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
        
        //add the popups container view controller's view to be the subview of popover
		[self.popoverVC.view addSubview:self.popupsContainerVC.view];
		
        //show popover
		[self.popover showRelativeToRect:self.searchRect ofView:self.mapView preferredEdge:NSMaxYEdge];
	}
	else { //there are no popups returned.
		
        //close existing popover
		[self.popover close];
		
        //show callout with a message
		self.mapView.callout.hidden = FALSE;
		self.mapView.callout.title = [NSString stringWithFormat:@"X: %.f Y: %.f", self.mapPoint.x, self.mapPoint.y];
		self.mapView.callout.detail = @"No Popups at this location!";
		[self.mapView.callout showCalloutAt:self.mapPoint screenOffset:CGPointMake(0, 0) animated:YES];
	}
}


#pragma mark - MapView Touch Delegate

// -------------------------------------------------------------------------------
//  mapView:didClickAtPoint:mapPoint:graphics
// -------------------------------------------------------------------------------
-(void)mapView:(AGSMapView *)mapView didClickAtPoint:(CGPoint)screen mapPoint:(AGSPoint *)mappoint features:(NSDictionary *)features{

    //set the mapPoint
	self.mapPoint = mappoint;
    
    //hide the callout
	self.mapView.callout.hidden = TRUE;
	
    //clear all popups
	if (self.popupsContainerVC) {
		[self.popupsContainerVC clearAllPopups];
	}
    
    //reset the subviews of popover so that the popups container view controller's view is removed
    //and the popover is prepared to display the new set of popups.
	if ([self.popoverVC.view.subviews count] > 0) {
		[self.popoverVC.view setSubviews:[NSArray array]];
	}
    //create a bouding rectangle for the mapPoint with a small buffer, and fetch popups for this envelope.
	int buffer = 5;
	self.searchRect = CGRectMake(screen.x - buffer,screen.y - buffer,buffer*2,buffer*2);
	[self.webMap fetchPopupsForExtent:[self.mapView toMapEnvelope:self.searchRect]];
}


#pragma mark - Show Error

// -------------------------------------------------------------------------------
//  showError:withMessageText
// -------------------------------------------------------------------------------
- (void)showError:(NSError*)error withMessageText:(NSString*)messageText {
    
    NSAlert *alert = [[NSAlert alloc] init];
    [alert setMessageText:messageText];
    [alert setInformativeText:[NSString stringWithFormat:@"%@",error]];
    [alert beginSheetModalForWindow:self.view.window modalDelegate:self didEndSelector:nil contextInfo:nil];
}

#pragma mark - Respond to Zoom
// -------------------------------------------------------------------------------
//  respondToZoom:aNotification
// -------------------------------------------------------------------------------
- (void)respondToZoom:(NSNotification*)aNotification {
    //if close popover if visible
    if (self.popover.shown) {
        [self.popover close];
    }
}

#pragma mark - Dealloc

// -------------------------------------------------------------------------------
//  dealloc
// -------------------------------------------------------------------------------
- (void)dealloc {
    
    //remove observer
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

@end
//OBJECTIVE C SAMPLE CODE
/*
 Copyright 2013 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
 
 http://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 <Cocoa/Cocoa.h>

@interface PopupsSample : NSViewController <AGSWebMapDelegate,AGSMapViewTouchDelegate>

@property (strong) IBOutlet AGSMapView *mapView;
@property (strong) IBOutlet NSViewController *popoverVC;
@property (strong) IBOutlet NSPopover *popover;
@property (strong) AGSWebMap *webMap;
@property (strong) AGSPopupsContainerViewController *popupsContainerVC;

@end
Feedback on this topic?