ThinkGeo.com    |     Documentation    |     Premium Support

Forcing Server-Side Layer Redraw when Using CallbackRequest and CustomColumnFetch

Hello!


This is something that has been bothering me for a few months.  I had a temporary solution but it is time to optimize the speed of our mapping application, so I want to see if you guys can help with a better solution.


Here is the situation:


I have a map which uses Callback Requests to change stuff on the back end.  But after the Callback Request returns, and I call the OpenLayers API redraw( ) method, it doesn't work.


 


 


My dilemma is that if I call Generate_Map( ), it works.  But this is a VERY SIMPLIFIED example.  I have about 20 other layers on my map, and Generate_Map( ) has to reload all of them.  Is there a way to do something the way Refresh_Map( ) works.  It seems like it is just ignoring me every time I tell it to Redraw( ).






 




public partial class __Default : System.Web.UI.Page, ICallbackEventHandler
{
Map tgMap; //This is only here for the sample code... In reality this is defined in the ASPX file...
Dictionary<string, string> zipTerritory = new Dictionary<string, string>( );

protected void Page_Load( object sender, EventArgs ea )
{
if ( !IsPostBack )
{
ZipTerritory_Load( );
Generate_Map( );
}
}

protected void ZipTerritory_Load( )
{
SqlDataReader sqlDataReader = new SqlDataReader( );

/*
...
Some code has been removed for brevity where  sqlDataReader" is loaded with data!
...
*/

zipTerritory.Clear( );
while ( sqlDataReader.Read( ) )
{
zipTerritory[sqlDataReader["Zip"].ToString( )] = sqlDataReader["Color"].ToString( );
}
}

protected void Generate_Map( )
{
ShapeFileFeatureLayer zipShape = new ShapeFileFeatureLayer( /* ... Omitted ... */ );
zipShape.FeatureSource.CustomColumnFetch += new EventHandler<CustomColumnFetchEventArgs>( Zip_CustomColumnFetch );

ValueStyle zipTerritoryColor = new ValueStyle( "TerritoryId", new Collection<ValueItem>( ) );

zipTerritoryColor.ValueItems.Add( new ValueItem( "1", new AreaStyle( new GeoSolidBrush( GeoColor.StandardColors.Red ) ) ) );
zipTerritoryColor.ValueItems.Add( new ValueItem( "2", new AreaStyle( new GeoSolidBrush( GeoColor.StandardColors.Red ) ) ) );
zipTerritoryColor.ValueItems.Add( new ValueItem( "3", new AreaStyle( new GeoSolidBrush( GeoColor.StandardColors.Red ) ) ) );

zipShape.ZoomLevelSet.ZoomLevel01.CustomStyles.Add( zipTerritoryColor );
zipShape.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20

LayerOverlay mainOverlay = new LayerOverlay( "Main", true, TileType.MultipleTile );

mainOverlay.Layers.Add( "Zip", zipShape );

tgMap.CustomOverlays.Clear( );
tgMap.CustomOverlays.Add( mainOverlay );
}

protected void Zip_CustomColumnFetch( object sender, CustomColumnFetchEventArgs ea )
{
FeatureSource zipShape = (FeatureSource) sender;
Feature zipFeature = zipShape.GetFeatureById( ea.Id, ReturningColumnsType.AllColumns );

string zipCode = zipFeature.ColumnValues["ZIP"];

if ( ea.ColumnName.StartsWith( "TerritoryId" ) )
{
ea.ColumnValue = zipTerritory[zipCode];
}
}

private string callbackResult = "";

public string GetCallbackResult( )
{
return callbackResult;
}

public void RaiseCallbackEvent( string callbackRequestString )
{
string[] data = callbackRequestString.Split( new char[] { '|' } );
string zipCode = data[0];
string territoryId = data[1];

zipTerritory[zipCode] = territoryId;

SqlCommand cmd = new SqlCommand( /* ... */ );
cmd.Execute( string.Format( "UPDATE SET territoryId = {0} WHERE zip = {1}", territoryId, zip ) ); //SIMPLIFIED EXAMPLE


Generate_Map( ); // THIS WORKS, but I am starting from SCRATCH.  SUB-OPTIMAL.

/*
But would prefer to do to this:
*/
Refresh_Map( ); // Would prefer to simply NUDGE the server to refresh only what I need
}

public void Refresh_Map( )
{
LayerOverlay layerOverlay = (LayerOverlay) tgMap.CustomOverlays["Main"];
ShapeFileFeatureLayer layer = (ShapeFileFeatureLayer) layerOverlay.Layers["Zip"];

layerOverlay.Redraw( ); // DOES NOT WORK
layer.FeatureSource.GeoCache.Clear( ); // DOES NOT WORK
}
}






function LayerRedraw( name, callback )
{
var l = OpenLayersMap.getLayer( name );

if ( l )
{
if ( l.mergeNewParams )
{
l.mergeNewParams( { 'version' : Math.random( ) } );
}

l.redraw( );
}
}

 


 



I didn't include the part which shows the callback, because you've seen the code many times already, most likely.  Here it is just in case.


 


 




function SwitchTerritory( zip, territoryId )
{
function MapRefreshCallback( result, refreshCallback )
{
LayerRedraw( "Main" );
}

var state = zip + "|" + territoryId; //Move Zip

WebForm_DoCallback( '__Page', state, MapRefreshCallback, null, null, true );
}


 


 


I'm pretty sure the problem is not with my script.  There's something going on the back-end that is causing the server to ignore me.

So I’m debugging in Visual Studio and it looks like when I set the Dictionary’s value in the Callback, it isn’t getting picked up by the other threads, since ThinkGeo is using multithreading to render the tiles.

No answer so I went at it alone with a crazy hack.



I figured when the threads were being created, the Map object was being passed to each one.  So I created a dummy FeatureSource I could use.

public class AlignmentDataLayer: FeatureLayer
{
public AlignmentDataLayer( IEnumerable<FeatureSourceColumn> featureSourceColumns )
{
this.FeatureSource = new AlignmentDataSource( featureSourceColumns );
}
}

public class AlignmentDataSource: InMemoryFeatureSource
{

public AlignmentDataSource( IEnumerable<FeatureSourceColumn> featureSourceColumns )
: base( featureSourceColumns )
{
}

public Hierarchy Hierarchy = new Hierarchy( );
public StringDictionary TerritoryZip = new StringDictionary( );
public Collection<Style> CustomStyles = new Collection<Style>( );
}


Now, from my C# code-behind, I can place arbitraty properties into this so they get passed around with the map. It's ugly but it works better than having to refresh the whole map.



public Hierarchy ScenarioHierarchy
{
get
{
return Alignment.Hierarchy;
}
}

protected StringDictionary ZipZone
{
get
{
return Alignment.TerritoryZip;
}
}

public AlignmentDataSource Alignment
{
get
{
LayerOverlay dataOverlay;
AlignmentDataLayer dataLayer;

if ( Map.CustomOverlays.Contains( "Data" ) )
{
dataLayer = (AlignmentDataLayer) ( (LayerOverlay) Map.CustomOverlays["Data"] ).Layers["Data"];
}
else
{
dataLayer = new AlignmentDataLayer( new Collection<FeatureSourceColumn>( ) );
dataOverlay = new LayerOverlay( "Data", false, TileType.SingleTile );
dataOverlay.IsVisible = false;
dataOverlay.IsVisibleInOverlaySwitcher = false;
dataOverlay.Layers.Add( "Data", dataLayer );

Map.CustomOverlays.Add( dataOverlay );
}

return (AlignmentDataSource) dataLayer.FeatureSource;
}
}



Hi Abner, 
  
 Sorry I reply you late, your post come in our best busy day. 
  
 I remembered if call redraw in openlayers, you need add a bool value as the parameter, like layerOverlay.Redraw(true); 
  
 Could you have a try that? 
  
 Regards, 
  
 Don