So I need to create a layer with thousands of markers. The InMemoryMarkerOverlay adds these (as well as their popups) to the DOM. I’d rather avoid that, and create an InMemoryFeatureLayer with a DefaultPointStyle set to a blue marker gif.
However, I also need some measure of interactivity. I thought I could add an OnClientClick script to the map and make an ajax call to determine if I had clicked on the marker. But how do I do that? Is there a way to determine if the region occupied by the marker for that point was where the click event occurred? Or is there another way to accomplish what I need to do?
Thanks.
Thousands of markers, but interactivity
Hi Jay,
Have you tried our ClusterMarkerStyle? Which should be helpful for solve show many markers in same level.
About call via AJAX I found some topics about that:
thinkgeo.com/forums/MapSuite/tabid/143/aft/11087/Default.aspx
thinkgeo.com/forums/MapSuite/tabid/143/aft/11997/Default.aspx
thinkgeo.com/forums/MapSuite/tabid/143/aft/7800/Default.aspx
And there should be more related topics about this problem in forum, wish they are helpful.
Regards,
Don
Jay,
I want to do some comments on Ajax call to implement the interactivity. You can check our HowDOI sample and navigate to InteractiveMap=>AddAClickEvent sample, then you should see we do an ajax call:
function mapClick(e) {
var params = { x: e.worldXY.lon, y: e.worldXY.lat };
Map1.ajaxCallAction(’@ViewContext.RouteData.Values[“Controller”].ToString()’, ‘ClickEvent’, params, mapCallback);
}
In the server side, we are using MapActionFilter attribution to catch call:
[MapActionFilter]
public void ClickEvent(Map map, GeoCollection<
object
> args)
{
PointShape position = new PointShape(Convert.ToDouble(args[0]), Convert.ToDouble(args[1]));
InMemoryMarkerOverlay markerOverlay = (InMemoryMarkerOverlay)map.CustomOverlays[“MarkerOverlay”];
markerOverlay.FeatureSource.InternalFeatures.Add(“marker” + Guid.NewGuid().ToString(), new Feature(position));
}
For your case, if you want to get the clicked marker information, we had a similar snippet codes, hope it helps:
[MapActionFilter]
public string ClickEvent(Map map, GeoCollection<
object
> args)
{
string returnStr = string.Empty;
PointShape position = new PointShape(Convert.ToDouble(args[0]), Convert.ToDouble(args[1]));
LayerOverlay overlay = (LayerOverlay)map.CustomOverlays[“CityOverlay”];
ShapeFileFeatureLayer inm = overlay.Layers[0] as ShapeFileFeatureLayer;
inm.Open();
double currentScale = ExtentHelper.GetScale(map.CurrentExtent, (float)map.WidthInPixels, GeographyUnit.DecimalDegree);
float standardDpi = 96f;
double inchToMeter = 0.0253999862840074;
double distance = currentScale * inchToMeter / standardDpi * 10;
Collection<
Feature
> closestFeature = inm.FeatureSource.GetFeaturesWithinDistanceOf(position, GeographyUnit.DecimalDegree, DistanceUnit.Meter, distance, ReturningColumnsType.AllColumns);
if (closestFeature.Any())
{
returnStr = "this is " + closestFeature[0].Id;
}
return returnStr;
}
Thanks,
Troy
Hi Troy. Thanks for the example. It seems like this might be a viable alternative.
I do have a couple questions about the ClusterMarkerStyle approach, though. Is it possible to combine that with something like the SizePointStyle sample, where the size would be controlled by the number of features clustered rather than a column value? I didn’t see how to get how many features were represented by a cluster.
Hi Jay,
The ClusterMarkerStyle control shows how many features by the property DistanceInPixel, if too many features in same location so it will shows only one feature. So I think we cannot modify the ClusterMarkerStyle to handle that by number.
If you want to implement that, maybe you need to create another custom style and override related layer render code for make that works, it’s complex.
Regards,
Don
Hi Don. Yes, I understand the DistanceInPixel setting controls how many features get clustered into one. But presumably, the style has to go through and find those features to cluster, and in doing so, it seems like it wouldn’t be too much of a stretch for it to keep track of how many features are being clustered at that point. Then it could somehow allow access to that number.
Thanks,
Jay
Hi Jay,
I read some related logic today, it looks the ClusterMarkerStyle don’t contains render related logic, the Cluster function is an function in an internal class, it based on the index system.
I think maybe we can discuss about whether we can add a number control property for this style, here we have some problems for it:
1. The ClusterMarkerStyle is for get a better render and better performance, so if we set the number, it maybe allowed the user set a group of the number for each zoomlevel, because under different zoomlevels, the distance between two markers is not the same. For get same effect, maybe in high zoomlevel we need to cluster 10000 markers, but in the low zoomlevel we only need to cluster 10 markers.
2. If in a high zoomlevel, there are about 10000 markers in same position, we set the DistanceInPixel for make it looks better, then we set the cluster number is 1000, then after cluster 1000 markers, the 10 new cluster marker still looks in the same position, it don’t looks better and performance still not so well. And the two property make confused if they work together.
So I think the currently property DistanceInPixel is enough for ClusterMarkerStyle, if you think a number control is important for you environment, maybe the solution should try to build a new class in your logic.
Regards,
Don
Ok, Don, thanks. I’ll keep experimenting with the approach Troy gave, which I’m noticing, has a slight issue:
double currentScale = ExtentHelper.GetScale(map.CurrentExtent, (float)map.WidthInPixels, map.MapUnit);
This never seems to change as I zoom in. So the area around what I’m clicking grows so that I can be quite a ways off and still have it resolve to that feature, which seems wrong.
Hi Jay,
You can found the map.CurrentExtent value hadn’t changed in server side.
For MVC, the client value change won’t be synchronous to server side unless you pass that back via AJAX. So you should want to directly read the scale value from OpenLayers API then pass that back via the params.
function mapClick(e) {
var params = { x: e.worldXY.lon, y: e.worldXY.lat };
Map1.ajaxCallAction(’@ViewContext.RouteData.Values[“Controller”].ToString()’, ‘ClickEvent’, params, mapCallback);
}
Wish that’s helpful.
Regards,
Don
Hi Don. Yes, that’s what I ended up doing, thanks. Out of curiosity, what value does the getResolution() client-side function represent?
Hi Jay,
I think it should based on OpenLayers API, because OpenLayers have a same API getResolution.
I am not very sure which calculatation we need the resolution value, but it looks the resolution represent map units per pixels which is an important value for OpenLayers.
Regards,
Don