Quantcast
Channel: Forums - ArcGIS Viewer for Flex
Viewing all articles
Browse latest Browse all 2097

Solution - switching between tiled services in different projections.

$
0
0
Hi everyone, I would like to share with you some thoughts about the topic of this discussion. We had to edit the Flex Viewer source code to enable changing map spatial reference for our project and I saw that a lot of people were asking similar question here so I think I can give you my solution.
First of all I would like to thank Dasa Paddock for some thoughts in this thread: http://forums.arcgis.com/threads/693...is-it-possible.
I'm aware my solution might not be the best solution out there but still it might give someone some insight on the topic. Before I start with some code I need to make sure you understand that this is solution specific for our project! Few things are needed but few things you will have to adjust to your needs(i will mark them in code).
Ok let's start:
Config.xml -> map tag, change the mapwraparound180 to false.
In MapManager.mxml we defined these variables:
Code:

//needed
private var opLayerArr:Array = new Array();
private var timer:Timer = new Timer(200); // delay of the timer is
private var visibleLayers:ArrayCollection;
private var mPoint:MapPoint;
private var visibleBasemaps:ArrayCollection = new ArrayCollection();
private var tiled:String = "tiled";
private var osm:String = "osm";
private var bing:String = "bing";
//for our project
private var webMercator:Number = 102100;
private var pol92:Number = 2180;
private var Compass:String = "Compass";

Now i want to populate opLayerArr with operational layers loaded from config.xml. So in MapManager.mxml find function "addLayerToMap", change the definition of this function:
Code:

private function addLayerToMap(layerObject:Object, isOperationalLayer:Boolean):void
//next inside this function add:
if(isOperationalLayer == true)
{       
      opLayerArr.push(layer);
}

Now find functions: private function loadNextOpertionalLayer():void and private function loadNextBasemapLayer():void
Code:

private function loadNextBasemapLayer():void
{
      var basemapLayerObject:Object = m_basemapLayers[m_basemapLayerIndex];
      addLayerToMap(basemapLayerObject, false);
      configureLayerRenderer(basemapLayerObject, false);
}
private function loadNextOpertionalLayer():void
{
      var optLayerObject:Object = m_operationalLayers[m_operationalLayerIndex];
      addLayerToMap(optLayerObject, true);
      configureLayerRenderer(optLayerObject, true);
}

Now go to private function basemapSwitchHandler(event:AppEvent):void
Code:

if (layer.id == basemapLabel)
{
      if (layer.id == selectedLabel)
      {
          var layerType:String = configBasemaps[i].type; //get type of the basemap service
            LoadBaseMaps(selectedLabel, layerType, layer, i, layers);
//call LoadBaseMaps function
      }
      else
      {
            layer.visible = false;
      }
}

Now function LoadBaseMaps:
Code:

var currentCenterPoint:MapPoint = map.extent.center;
var currentWKID:Number = map.spatialReference.wkid;
if(layerType == tiled || layerType == osm || layerType == bing)
{
        var toWKID:Number;
        (selectedLabel == Compass) ? toWKID = pol92 : toWKID = webMercator; // ok this is for our project you
        // will have to adjust this to your needs- we know that only one mapservice has different spatial reference and rest is in
        // web mercator

        if(toWKID != currentWKID)
        {
                ProjectPoint(currentCenterPoint, toWKID);
                ChangeSpatialRef(toWKID);
                map.lods = layers[i].tileInfo.lods;
                visibleBasemaps.addItem(layer);
        }
        else
        {
                layer.visible = true;
                map.lods = layers[i].tileInfo.lods;
        }
}
else
{
        layer.visible = true;
}
//if layer is tiled(or bing or osm) do some logic, else just turn it visible

Now project map.extent.center to new spatial reference so we can pan the map to the right place:
Code:

private function ProjectPoint(mapPoint:MapPoint, toWKID:Number):void
{
        try
        {
                var projectParams:ProjectParameters =  new ProjectParameters();
                projectParams.geometries = [mapPoint];
                projectParams.outSpatialReference = new SpatialReference(toWKID);
                geometryService.project(projectParams);
        }
        catch(er:Error)
        {
                //your logic for error                       
        }
}

Next is ChangeSpatialRef function:
Code:

private function ChangeSpatialRef(toWKID:Number):void
{
        map.spatialReference.wkid = toWKID;
        RefreshLayers();
}

After changing spatial reference we need to refresh all operational layers plus I want to 'remember' which op layers were visible:
Code:

private function RefreshLayers():void
{
        visibleLayers = new ArrayCollection();
        for each (var layer:Layer in opLayerArr)//this is what why we need the opLayerArr                                       
        {
                for each(var layerW:Layer in map.layers)
                {
                        if(layer == layerW)
                        {
                                if(layerW.visible == true)
                                {
                                        visibleLayers.addItem(layerW);
                                        layerW.visible = false;
                                }
                                layerW.refresh();
                        }
                }
        }
}

Now when the Geometry Service completes projection do this:
Code:

private function onProjectComplete(event:GeometryServiceEvent):void
{
        mPoint = event.result[0] as MapPoint;
        map.centerAt(mPoint);
        timer.start();//i want to be sure that map recentered so i will check it with timer
        // in private function this_creationCompleteHandler():void add -> timer.addEventListener(TimerEvent.TIMER, CheckExtent);
}

Ok so let's check the extent(to be precise - let's check the center point of extent). I dont know why but honestly from the beginning there was a problem with recentering the map in onProjectComplete function so we had to add timer to make sure the extent will change.
Code:

private function CheckExtent(event:TimerEvent):void
{
        if(map.extent.center != mPoint)
        {
                map.centerAt(mPoint);
                SetLayersVisible();
                timer.stop();
        }
        else
        {
                SetLayersVisible();
                timer.stop();
        }
}

And the last function(note: I wanted to be sure that I turn on layers only when the extent is correct so making them visible is the last thing to do)
Code:

private function SetLayersVisible():void
{
        for each(var l:Layer in visibleBasemaps)
        {
                l.visible = true;
        }
        for each(var layer:Layer in visibleLayers)
        {
                for each(var lay:Layer in map.layers)
                {
                        if(layer == lay)
                        {
                                lay.visible = true;
                        }
                }
        }
        visibleBasemaps.removeAll();
        visibleLayers.removeAll();
}

So this is it. I'm aware many of you would do this better, faster etc. but this is a discussion so you can point me few things that should be done different.
Few notes:
1.You will have to deal with LODs for dynamic/wms map services - if you don't want them just clear the map.lods array -> you can show dynamic services in scales of one of your tiled service.
2.We tested this with LayerListWidget, Legend and few other(CoordinateMenuWidget, BookmarkWidget[*], DrawAndMeasureWidget..) and they all works. No errors at all.
3.As I said - adjust this to your project!
4.Solution for OverviewMap in next post - this is already too long;)
*In bookmark widget you'll have to make few changes to make it work in all spatial refenerces in your project.

Regards
MDruzgala

Viewing all articles
Browse latest Browse all 2097

Trending Articles