Tuesday, March 12, 2013

Using Mapguide selection with OpenLayers



Using Mapguide selection with OpenLayers

Mapguide is a great solution for building web applications:  it has the best administrative interface of all open-source (and some commercial) web servers, it is very fast with vector data, it integrates nicely with AutoCAD Map 3D, ...
Even though I am very satisfied with the basic viewer, it does not play well on mobile devices. Creating a viewer using OpenLayers seems the way to go, and it is in fact the library used by the fusion viewer and AutoDesk's own mobile viewer.
Since I wanted my mobile viewer integrated with an existing application, I prefered rolling my own viewer based on OpenLayers: for integration with the rest of an existing webapplication I prefer a simple viewer rather than having all the bells and whistles of the fusion viewer.

Creating only a viewer without interactions is easy, just looking to the sample code of the mapguide on on OpenLayers will work.

Things get more interesting when you want to use more of mapguide functionality, eg using selections. 
The openlayers api documentation is not very helpful: "Some server plumbing is required to read such a value".
But which plumbing is required?
Ok let's try to find out:  For selection to work  a session must be created, and in this session we can create a map with its own selection object. This is actually the "server plumbing" required.
<?php
 try
    {
        include 'C:\Program Files\OSGEO\mapguide\www\mapadmin/constants.php';
 
        $locale = "en"; // localizable string
        $errorMsg = "";
 
        // Initialize web tier with the site configuration file.  The config
        // file should be in the directory above as this script.
        $webConfigFile = __FILE__;
        $pos = strrpos($webConfigFile, '\\');
        if ($pos == false)
            $pos = strrpos($webConfigFile, '/');
 
        $relativeLocation = '../'.MgConfigProperties::DefaultConfigurationFilename;
        $webConfigFile = substr_replace($webConfigFile, $relativeLocation, $pos+1);
        MgInitializeWebTier($webConfigFile);
    }
    catch ( MgException $e )
    {
        $errorMsg = $e->GetExceptionMessage();
        echo $errorMsg;
    }
    catch ( Exception $e )
    {
        $errorMsg = $e->getMessage();
        echo $errorMsg;
    }
   
     // Establish a connection with a MapGuide site.
     $user = new MgUserInformation('Anonymous', '');
     $siteConnection = new MgSiteConnection();
     $siteConnection->Open($user);
// Create a session repository
$site      = $siteConnection->GetSite();
$sessionID = $site->CreateSession();
$user->SetMgSessionId($sessionID);
// Get an instance of the required services.
$resourceService = $siteConnection->CreateService(MgServiceType::ResourceService);
$mappingService  = $siteConnection->CreateService(MgServiceType::MappingService);

// Get a runtime map from a map definition
$resourceID = new MgResourceIdentifier('Library://MapsOnline/MapNL_intern.MapDefinition');
$map        = new MgMap();
$map->Create($resourceService, $resourceID, 'MapsonlineOL');
$mapName = uniqid($mapTitle);

$mapStateId = new MgResourceIdentifier("Session:" . $sessionID . "//" . $mapName . "." . MgResourceType::Map);


//create an empty selection object and store it in the session repository
$sel = new MgSelection($map);
$sel->Save($resourceService, $mapName);


$map->Save($resourceService, $mapStateId);
// Show information about the map
header("Content-type: application/json");
echo json_encode(array(
    "map" => $mapName,
    "sessionId" =>$sessionID
));
?>

The next thing which is needed is an event handler on the clientside in OpenLayers itself. For this example I extended the default selectioncontrol to run the function "selectRectangle" if a selection is made with the shift-button down.

Next step is implementing this selectRectangle function. Thisfunction will pass the bounding box of the rectangle to the mapagent.  Note that there is a random seq value added to the request: this is in place to make sure that no caching is done.
       function selectRectangle(bounds) {
            var ll = map.getLonLatFromPixel(new OpenLayers.Pixel(bounds.left, bounds.bottom));
            var ur = map.getLonLatFromPixel(new OpenLayers.Pixel(bounds.right, bounds.top));
            var polygon = "POLYGON((" + ll.lon.toFixed(4) + " " +
                              ll.lat.toFixed(4) + ", " +
  ll.lon.toFixed(4) + " " +
                              ur.lat.toFixed(4) + ", " +
  ur.lon.toFixed(4) + " " +
                              ur.lat.toFixed(4) + ", " +
                              ur.lon.toFixed(4) + " " +
                              ll.lat.toFixed(4) + ", " +
  ll.lon.toFixed(4) + " " +
                              ll.lat.toFixed(4) + "))"; ;
            $.post(mapagent, { SESSION: session, MAPNAME: mapname, GEOMETRY: polygon    'MAXFEATURES': '-1', OPERATION: 'QUERYMAPFEATURES', PERSIST: '1', SEQ: Math.random(), VERSION: '1.0.0', SELECTIONVARIANT: 'INTERSECTS', CLIENTAGENT: "openlayers"
},
showSelection, "xml"
);
        }
After posting this form, the selection state is saved in  the server session, but not yet visible on the client. Therefore we must redraw our dynamic layer (which contains the selection).  I'm also calling a url from the basic viewer which will return me a json object of the selected features (wishlist: this function should be present in the mapagent).  Finally I use a function to render this json to a table, so I can see what is actually selected :-)
        function showSelection(data) {
            dynlayer.redraw(true);
            url = "/mapguide/mapviewerphp/getselectedfeatures.php
            $.get(url, { SESSION: session, MAPNAME: mapname, LOCALE: 'en', SEQ: Math.random() }, selectionJsonToTable, "json");
        }



Interested in running your own version? Download the full code here, it will also require jQuery and Openlayers.

Stay tuned for my next blogpost where I will expand this viewer to an editor with snapping enabled!

3 comments:

  1. Nice to see another MapGuide blog around here :)

    ReplyDelete
  2. Nice method. We found one issue and I don't know if it is on ours only or what, but on the 2.5.1 install, the Anonymous account has issues with allowing a map to display. As soon as we switch it to like Administrator, everything works without changing anything else. Just wanted to pass this along to anyone else that might run into the same issue. Again, great method! Post more, this was very helpful.

    ReplyDelete
  3. The createsessionandmap.php on GITHUB is different from the code here. For example, rather than JSON, it returns the following.

    // Show information about the map
    echo "Name of map: '" . $map->GetName() . "'n";
    echo " Session ID of map: " . $map->GetSessionId() . "n";
    echo " Object ID: " . $map->GetObjectId() . "n";


    Sweet solution otherwise!

    ReplyDelete