ThinkGeo.com    |     Documentation    |     Premium Support

InMemoryMarkerOverlay Unique Popups

Hello,


I need the ability to display (lots) of markers and SimpleMarkerOverlay() will not work due to the speed issues when displaying large quantities of data.


I have decided to pursue displaying this information using InMemoryMarkerOverlay but I am not sure how I would display unique popup information depending on the marker you are mousing over.  I can add one PointMarkerStyle but that is for the whole group of markers which isn't going to work.


The information that I am adding to the markers is the result of a SQL query using a dataReader. 


 


Thanks much in advance,


Kevin



Hi Kevin,


I would ask when do you get the result of the SQL Query, is it finished while adding markers? If so, you can add a customized column to store the popup information and use the following code to display different popups for different markers. 


InMemoryMarkerOverlay markerOverlay = new InMemoryMarkerOverlay("MarkerOverlay");
// Add a customized column
markerOverlay.Columns.Add(new FeatureSourceColumn("YourOwnColumn"));
Feature feature = new Feature(-94.48242, 38.75977);
// Add customized popup content to this column
feature.ColumnValues["YourOwnColumn"] = "ColumnValue";
markerOverlay.Features.Add("Kansas", feature);
markerOverlay.ZoomLevelSet.ZoomLevel01.DefaultMarkerStyle.WebImage = new WebImage("../../theme/default/img/marker_blue.gif", 21, 25);
markerOverlay.ZoomLevelSet.ZoomLevel01.DefaultMarkerStyle.Popup.ContentHtml = "[#YourOwnColumn#]";
markerOverlay.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;


Regards,


Ivan



Hey! 
  
 Awesome!  Works great :) 
  
 Had to add in: 
  
 markerOverlay.FeatureSource.Open();  
 Prior to adding the new FeatureSourceColumn. 
  
 Thanks a bunch! 
 Kev 


Ivan, thanks for the idea.  That's a good solution.


Kevin, you're correct, it's good to explicitly call the markerOverlay.FeatureSource.Open();   You may also want to call the Close() method when you're finished editing the FeatureSource.  If you forget to call Close(), everything will still work and no errors will be thrown.  But it's just good practice to call Close() anytime you call Open().


Let us know if there is anything else we can do to help.  Thanks,


 


Phil



No problem! 
  
 And I do have another question… Would it be possible to cluster the features like this: 
  
 gis.thinkgeo.com/Support/DiscussionForums/tabid/143/aff/21/aft/6295/afv/topic/Default.aspx 
  
 Using ClusterPointStyle? 
  
 Thanks so much, 
 Kev

OK OK… I have one more question! 
  
 I am using: 
  
 OnExtentChanged="Map1_ExtentChanged" 
  
 With this on the MarkerOverlay.Features added to the map blink every time the maps view extent is changed…  
 Is there a way to stop this from happening without removing the OnExtentChanged="Map1_ExtentChanged"? 
  
 Cheers & Many Thanks, 
 Kevin

Kevin,


In regards to your ClusterStyle question, the samle you linked to will only work for our regular PointStyles.  It will not work in an InMemoryMarkerOverlay.  However, there is a ClusterMarkerStyle  that you can use with the InMemoryMarkerOverlay.  I believe the ClusterMarkerStyle will hide some markers based on their proximity to other markers.  It does not group markers and give a count like David's sample does.  Do you need there to be a grouping and count?  If so, we may be able to work up a Custom ClusterMarkerStyle example for you, but I'm not sure how you would expect the ContentHtml to behave if there are multiple features grouped together.


On the ExtentChanged question, you're correct.  If you use the map's ExtentChanged property it will do a postback each time the map's extent changes.  This will cause the map to flicker.  However, I would recommend using the “OnClientExtentChanged” property to raise a callback.  This will eliminate the flicker and do everything with an asynchronous AJAX call.  An example of this callback method can be found under Howard's 6/26 post at:  gis.thinkgeo.com/Support/Dis...fault.aspx


Thanks,


Phil



 Hey Phil,


 
Thanks so much for the added information!
 
I've played quickly with ClusterMarkerStyle... Will I be able to have custom popup windows with Clusters? 
 Currently I am doing as discussed above:
 

 Feature feature = new Feature(projPointShape);
 feature.ColumnValues["YourOwnColumn"] = CompanyClick;
 markerOverlay.Features.Add(feature);
 currentCluster.MarkerStyle = new PointMarkerStyle(new WebImage("../theme/default/img/house.png", 15, 15));

But now the pop-ups wont show even because I have to set the markerOverlay.CustomMarkerStyle = ClusterMarkerStyle;
 
I am sure I am missing something easy...
 
Thanks in advance,
Kev

Hey again, 



So I solved that issue, it was pretty quick. 


Feature feature = new Feature(projPointShape);
feature.ColumnValues["YourOwnColumn"] = CompanyClick;
markerOverlay.Features.Add(feature);
PointMarkerStyle markerStyle = new PointMarkerStyle(new WebImage("../theme/default/img/house.png", 15, 15));
markerStyle.Popup.ContentHtml = "[#YourOwnColumn#]";
markerStyle.Popup.AutoSize = true;
currentCluster.MarkerStyle = markerStyle;  

But it is pretty hard to judge what your looking at without there being a count... a Custom ClusterMarkerStyle might just do the trick so the user has an idea of what he is zooming into. 



Thanks so much for the help as always and let me know what you think. 



Cheers, 

Kevin



Kevin, 
  
 I just have a couple of questions for you before we head down the wrong road.  Are you using the InMemoryMarkerOverlay so that you can have the ability to show a popup on the mouseover?  If we are able to develop a custom CluserMarkerStyle, and we have 5 markers grouped into 1, what behavior would you expect when there is a mouseover?  Would it show the ContentHtml of all 5 markers?   Or would you only want the mouseover to popup content once the user has zoomed in far enough so that there is only 1 marker instead of a cluster? 
  
 If that’s what you need, then I think you’ll need to override the InMemoryMarkerOverlay and override the GetMarkersCore method.  It’s a little more complicated than a custom style.  If you need any help with this, feel free to contact our Professional Services department and they can assist you.  
  
 Thanks, 
  
 Phil 
  


Hey Phil, 
  
 We are using unique markers to represent a specific item so having a number signifying the number of items underneath would be great when user is at a macro level.   
  
 When I mouse over the blob of items nothing needs to be displayed until the user has zoomed in to a closer extent. 
  
 I will contact the professional services department to get a hand with this…  
  
 BUT I do have another question for you! 
  
 I have written my code to now include OnClientExtentChanged.  It works well but has one caveat, it causes the program to run very slow at times and I believe this is because it calls the server side function lots and lots of times for every movement the map made. 
  
 Is there a method call that runs only when the map is finished moving so instead of hundreds of server side calls you only have one for each movement. 
  
 Thanks, 
 Kevin 
  
  


 Kevin,


 
I'm glad you posted this.  I tested the OnClientExtentChanged and it fires multiple times as the map is panning.    If you have a slow-running process on the server side, this could be a big problem.
 
I've found a much better solution.  Instead of using the OnClientExtentChanged, you should hook into the OpenLayers 'moveend' event.  This will only get fired once after the map is finished panning, or finished zooming.  To implement the moveend event, You need to register the event in the OnmapCreating function.   You'll also need to create a function to get called once the callback completes.  The client side Javascript looks like the example below:  

function OnMapCreating(map) {
            map.events.register('moveend', map, function (e) {        
                var args = map.getExtent().toGeometry().toString();
                <%=ClientScript.GetCallbackEventReference(this, "args", "Map1ExtentChangedCompleted", "map") %>
            });
        }
    
        var Map1ExtentChangedCompleted = function(result, context){
            alert(result);
        }



Your server side code inside your .aspx.cs code behind page will not change from Howard's example.  You'll still need to impelement ICallbackEventHandler and implement the GetCallbackResult() and RaiseCallbackEvent() methods.

Let me know if you have any questions with the sample above and I can post a complete sample for you to test.

Thanks,
Phil

Hey Phil, 
  
 Thanks for the great idea! 
  
 I tried implementing this but for some reason it won’t register the moveend call.   
  
 An example would be very helpful if you don’t mind! 
  
 Cheers, 
 Kevin 
  


 Kevin,


Attached is a sample .aspx page that will trigger a callback on the MoveEnd event.  The server-side will generate a test string and send it back to the client.  Then the client will display this string in an alert box.  This should only fire one time - after the user has finished panning or zooming.


Let me know how it works.


Thanks,


 


Phil



MoveEndCallback.zip (1.98 KB)

 Hey Phil,


Works great!


I had a little issue getting it to work with another set of js code:


 


 


Only one of those js functions is working...the last one called.  I tried putting them together and then one after each other.  Will these js functions work together?


 


Thanks for all the help Phil, it is truly appreciated.


Kevin



        var OnMapCreating = function (map) {
            OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control.OverviewMap, {
                updateOverview: function () {
                    var zoom = map.getZoom();
                    var targetRes = this.map.getResolutionForZoom(4);
                    var center;
                    if (this.ovmap.getProjection() != this.map.getProjection()) {
                        center = this.map.center.clone();
                        center.transform(this.map.getProjectionObject(), this.ovmap.getProjectionObject());
                    }
                    else {
                        center = this.map.center;
                    }
                    this.ovmap.setCenter(center, this.ovmap.getZoomForResolution(targetRes * this.resolutionFactor));
                    this.updateRectToMap();
                }
            });
            OpenLayers.Control.PanZoomBar.prototype.buttonDown = function (evt) {
                if (!OpenLayers.Event.isLeftClick(evt)) {
                    return;
                }
                switch (this.action) {
                    case "panup":
                        this.map.pan(0, -this.getSlideFactor("h"));
                        break;
                    case "pandown":
                        this.map.pan(0, this.getSlideFactor("h"));
                        break;
                    case "panleft":
                        this.map.pan(-this.getSlideFactor("w"), 0);
                        break;
                    case "panright":
                        this.map.pan(this.getSlideFactor("w"), 0);
                        break;
                    case "zoomin":
                        this.map.zoomIn();
                        break;
                    case "zoomout":
                        this.map.zoomOut();
                        break;
                    case "zoomworld":
                        var bounds = new OpenLayers.Bounds(-14281142.226965, 6100819.4578717, -10876716.354163, 8503943.6033795);
                        this.map.zoomToExtent(bounds);
                        break;
                }

                OpenLayers.Event.stop(evt);
            }
        }
     


Thanks Kevin, 
  
 Yes, you can use multiple functions like you’re attempting above.  I’ve tested your PanZoom script alongside the moveend script and they both work together.   
  
 I think the problem is with your first function above that deals with the Overview Map.  I have a couple of questions on this: 
    1. Are you trying to use a minimap?  It seems like there is some missing code in your example. 
    2. Have you tried using the built-in minimap by using Map1.MapTools.MiniMap.Enabled = true;  in the Page_Load of the .aspx.cs? 
  
 Sometimes using the minimap can be tricky.  If option 2 above does not help you, can you upload a simple sample project that shows your entire sampmle and I can try to figure it out. 
  
 Thanks, 
 Phil

 Hey Phil,


I got that all sorted out...It was just a matter of putting the two snippets of code in the correct place and in the correct order.  Basically the final solution looking like:


 Thanks again for all the help.  I do have a few more questions in regards to selecting information from a line layer file but Ill start a new thread.


Thanks,


Kevin



          function OnMapCreating(map) {
            map.events.register('moveend', map, function (e) {        
                var currentExtent = e.object.getExtent();
                var args = currentExtent.left + ',' + currentExtent.top + ',' + currentExtent.right + ',' + currentExtent.bottom;
                <%=ClientScript.GetCallbackEventReference(this, "args", "Map1ExtentChangedCompleted", "map") %>
            });
            OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control.OverviewMap, {
                updateOverview: function () {
                    var zoom = map.getZoom();
                    var targetRes = this.map.getResolutionForZoom(4);
                    var center;
                    if (this.ovmap.getProjection() != this.map.getProjection()) {
                        center = this.map.center.clone();
                        center.transform(this.map.getProjectionObject(), this.ovmap.getProjectionObject());
                    }
                    else {
                        center = this.map.center;
                    }
                    this.ovmap.setCenter(center, this.ovmap.getZoomForResolution(targetRes * this.resolutionFactor));
                    this.updateRectToMap();
                }
            });
             OpenLayers.Control.PanZoomBar.prototype.buttonDown = function (evt) {
                if (!OpenLayers.Event.isLeftClick(evt)) {
                    return;
                }
                switch (this.action) {
                    case "panup":
                        this.map.pan(0, -this.getSlideFactor("h"));
                        break;
                    case "pandown":
                        this.map.pan(0, this.getSlideFactor("h"));
                        break;
                    case "panleft":
                        this.map.pan(-this.getSlideFactor("w"), 0);
                        break;
                    case "panright":
                        this.map.pan(this.getSlideFactor("w"), 0);
                        break;
                    case "zoomin":
                        this.map.zoomIn();
                        break;
                    case "zoomout":
                        this.map.zoomOut();
                        break;
                    case "zoomworld":
                        var bounds = new OpenLayers.Bounds(-14281142.226965, 6100819.4578717, -10876716.354163, 8503943.6033795);
                        this.map.zoomToExtent(bounds);
                        break;
                }

                OpenLayers.Event.stop(evt);
            }
        }
        var Map1ExtentChangedCompleted = function(result, context){
            alert(result);
        }
   


Great!  Glad everything is working.  Thanks for starting the new thread for the new topic. 
  
  
 Phil

Hey, 
  
 Couple follow up questions in regards to this function: 
  
  function OnMapCreating(map) {
            map.events.register(‘moveend’, map, function (e) {        
                var args = map.getExtent().toGeometry().toString();
                <%=ClientScript.GetCallbackEventReference(this, “args”, “Map1ExtentChangedCompleted”, “map”) %>
            });
        }
    
        var Map1ExtentChangedCompleted = function(result, context){
            alert(result);
        } 
 
  
 I walked through this code in Visual Studio and if you pan the map the function in the code behind gets called many many times.  Is there a way to avoid this and have it only get called on the ‘moveend’? 
  
 Removing this code speeds up my program significantly but the functionality is still required. 
  
 Thanks in advance, 
 Kevin

Dear Kevin, 
  
 Sorry, ‘moveend’ event is define by OpenLayer, when you pan it should active many times depends how far you panned. 
  
 But you can add a flag to active it when you want. 
  
 Regards, 
  
 Gary