/// <summary> /// todo use threads and threadData /// </summary> /// <param name="points"></param> /// <param name="input"></param> public GridCluster(IList <P> points, JsonGetMarkersReceive input) : base(points) { this._jsonReceive = input; double[] deltas = GetDelta(GmcSettings.Get.Gridx, GmcSettings.Get.Gridy, input.Zoomlevel); DeltaX = deltas[0]; DeltaY = deltas[1]; Grid = GetBoundaryExtended(input); }
public GridCluster(IPoints dataset, JsonGetMarkersReceive jsonReceive) : base(dataset) { // Important, set _delta and _grid values in constructor as first step var deltas = GetDelta(jsonReceive); DeltaX = deltas[0]; DeltaY = deltas[1]; Grid = GetBoundaryExtended(jsonReceive); Lines = new List <Line>(); if (AlgoConfig.DoShowGridLinesInGoogleMap) { MakeLines(jsonReceive); } }
public static double[] GetDelta(JsonGetMarkersReceive jsonReceive) { // Heuristic specific values and grid size dependent. // used in combination with zoom level. // xZoomLevel1 and yZoomLevel1 is used to define the size of one grid-cell // Absolute base value of longitude distance const int xZoomLevel1 = 480; // Absolute base value of latitude distance const int yZoomLevel1 = 240; // Relative values, used for adjusting grid size var gridScaleX = AlgoConfig.Gridx; var gridScaleY = AlgoConfig.Gridy; var x = MathTool.Half(xZoomLevel1, jsonReceive.Zoomlevel - 1) / gridScaleX; var y = MathTool.Half(yZoomLevel1, jsonReceive.Zoomlevel - 1) / gridScaleY; return(new double[] { x, y }); }
public static Boundary GetBoundaryExtended(JsonGetMarkersReceive input) { var deltas = GetDelta(GmcSettings.Get.Gridx, GmcSettings.Get.Gridy, input.Zoomlevel); var deltaX = deltas[0]; var deltaY = deltas[1]; // Grid with extended outer grid-area non-visible var a = MathTool.FloorLatLon(input.Viewport.Minx, deltaX) - deltaX * GmcSettings.Get.OuterGridExtend; var b = MathTool.FloorLatLon(input.Viewport.Miny, deltaY) - deltaY * GmcSettings.Get.OuterGridExtend; var a2 = MathTool.FloorLatLon(input.Viewport.Maxx, deltaX) + deltaX * (1 + GmcSettings.Get.OuterGridExtend); var b2 = MathTool.FloorLatLon(input.Viewport.Maxy, deltaY) + deltaY * (1 + GmcSettings.Get.OuterGridExtend); // Latitude is special with Google Maps, they don't wrap around, then do constrain b = MathTool.ConstrainLatitude(b); b2 = MathTool.ConstrainLatitude(b2); var grid = new Boundary { Minx = a, Miny = b, Maxx = a2, Maxy = b2 }; grid.Normalize(); return(grid); }
void MakeLines(JsonGetMarkersReceive jsonReceive) { if (!jsonReceive.IsDebugLinesEnabled) { return; // client disabled it } // Make the red lines data to be drawn in Google map var temp = new List <Rectangle>(); const int borderLinesAdding = 1; var linesStepsX = (int)(Math.Round(Grid.AbsX / DeltaX) + borderLinesAdding); var linesStepsY = (int)(Math.Round(Grid.AbsY / DeltaY) + borderLinesAdding); var b = new Boundary(Grid); const double restrictLat = 5.5; b.Miny = MathTool.ConstrainLatitude(b.Miny, restrictLat); // Make sure it is visible on screen, restrict by some value b.Maxy = MathTool.ConstrainLatitude(b.Maxy, restrictLat); // Vertical lines for (var i = 0; i < linesStepsX; i++) { var xx = b.Minx + i * DeltaX; // Draw region if (jsonReceive.Zoomlevel > 3) { temp.Add(new Rectangle { Minx = xx, Miny = b.Miny, Maxx = xx, Maxy = b.Maxy }); } // World wrap issue when same latlon area visible multiple times // Make sure line is drawn from left to right on screen else { temp.Add(new Rectangle { Minx = xx, Miny = LatLonInfo.MinLatValue + restrictLat, Maxx = xx, Maxy = 0 }); temp.Add(new Rectangle { Minx = xx, Miny = 0, Maxx = xx, Maxy = LatLonInfo.MaxLatValue - restrictLat }); } } // Horizontal lines for (var i = 0; i < linesStepsY; i++) { var yy = b.Miny + i * DeltaY; // Draw region if (jsonReceive.Zoomlevel > 3) { // Don't draw lines outsize the world if (MathTool.IsLowerThanLatMin(yy) || MathTool.IsGreaterThanLatMax(yy)) { continue; } temp.Add(new Rectangle { Minx = b.Minx, Miny = yy, Maxx = b.Maxx, Maxy = yy }); } // World wrap issue when same latlon area visible multiple times // Make sure line is drawn from left to right on screen else { temp.Add(new Rectangle { Minx = LatLonInfo.MinLonValue, Miny = yy, Maxx = 0, Maxy = yy }); temp.Add(new Rectangle { Minx = 0, Miny = yy, Maxx = LatLonInfo.MaxLonValue, Maxy = yy }); } } // Normalize the lines and add as string foreach (var line in temp) { var x = (line.Minx).NormalizeLongitude().DoubleToString(); var x2 = (line.Maxx).NormalizeLongitude().DoubleToString(); var y = (line.Miny).NormalizeLatitude().DoubleToString(); var y2 = (line.Maxy).NormalizeLatitude().DoubleToString(); Lines.Add(new Line { X = x, Y = y, X2 = x2, Y2 = y2 }); } }
/// <summary> /// Read Through Cache /// </summary> /// <param name="input"></param> /// <returns></returns> public JsonMarkersReply GetMarkersHelper(JsonGetMarkersInput input) { try { var nelat = Math.Round(input.nelat.ToDouble(), Numbers.Round); var nelon = Math.Round(input.nelon.ToDouble(), Numbers.Round); var swlat = Math.Round(input.swlat.ToDouble(), Numbers.Round); var swlon = Math.Round(input.swlon.ToDouble(), Numbers.Round); var zoomLevel = int.Parse(input.zoomLevel); var filter = input.filter ?? ""; // values are validated there var inputValidated = new JsonGetMarkersReceive(nelat, nelon, swlat, swlon, zoomLevel, filter); var grid = GridCluster.GetBoundaryExtended(inputValidated); var cacheKeyHelper = string.Format("{0}_{1}_{2}", inputValidated.Zoomlevel, inputValidated.FilterHashCode(), grid.GetHashCode()); var cacheKey = CacheKeys.GetMarkers(cacheKeyHelper.GetHashCode()); var reply = _memCache.Get <JsonMarkersReply>(cacheKey); if (reply != null) { // return cached data reply.Cache = true; return(reply); } inputValidated.Viewport.ValidateLatLon(); // Validate google map viewport input (should be always valid) inputValidated.Viewport.Normalize(); // Get all points from memory IList <P> points = _pointsDatabase.GetPoints(); #region fiter // Filter points points = FilterUtil.FilterByType( points, new FilterData { TypeFilterExclude = inputValidated.TypeFilterExclude } ); #endregion filter // Create new instance for every ajax request with input all points and json data ICluster clusterAlgo = new GridCluster(points, inputValidated); var clusteringEnabled = inputValidated.IsClusteringEnabled || GmcSettings.Get.AlwaysClusteringEnabledWhenZoomLevelLess > inputValidated.Zoomlevel; // Clustering if (clusteringEnabled && inputValidated.Zoomlevel < GmcSettings.Get.ZoomlevelClusterStop) { #region cluster IList <P> markers = clusterAlgo.RunCluster(); #endregion cluster reply = new JsonMarkersReply { Markers = markers, Polylines = clusterAlgo.GetPolyLines(), }; } else { // If we are here then there are no clustering // The number of items returned is restricted to avoid json data overflow IList <P> filteredDataset = FilterUtil.FilterDataByViewport(points, inputValidated.Viewport); IList <P> filteredDatasetMaxPoints = filteredDataset.Take(GmcSettings.Get.MaxMarkersReturned).ToList(); reply = new JsonMarkersReply { Markers = filteredDatasetMaxPoints, Polylines = clusterAlgo.GetPolyLines(), Mia = filteredDataset.Count - filteredDatasetMaxPoints.Count, }; } // if client ne and sw is inside a specific grid box then cache the grid box and the result // next time test if ne and sw is inside the grid box and return the cached result if (GmcSettings.Get.CacheServices) { _memCache.Set(reply, cacheKey, TimeSpan.FromMinutes(10)); // cache data } return(reply); } catch (Exception ex) { return(new JsonMarkersReply { Ok = "0", EMsg = string.Format("MapService says: exception {0}", ex.Message) }); } }
// Post public JsonMarkersReply Markers( double nelat, double nelon, double swlat, double swlon, int zoomlevel, string filter, int sendid ) { var sw = new Stopwatch(); sw.Start(); var jsonReceive = new JsonGetMarkersReceive(nelat, nelon, swlat, swlon, zoomlevel, filter, sendid); var clusteringEnabled = jsonReceive.IsClusteringEnabled || AlgoConfig.AlwaysClusteringEnabledWhenZoomLevelLess > jsonReceive.Zoomlevel; JsonMarkersReply reply; jsonReceive.Viewport.ValidateLatLon(); // Validate google map viewport input (is always valid) jsonReceive.Viewport.Normalize(); // Get all points from memory IPoints points = MemoryDatabase.GetPoints(); if (jsonReceive.TypeFilterExclude.Count == AlgoConfig.MarkerTypes.Count) { // Filter all points = new Points(); // empty } else if (jsonReceive.TypeFilterExclude.Count > 0) { // Filter data by typeFilter value // Make new obj, don't overwrite obj data points = new Points { Data = points.Data .Where(p => jsonReceive.TypeFilterExclude.Contains(p.T) == false) .ToList() }; } // Create new instance for every ajax request with input all points and json data var clusterAlgo = new GridCluster(points, jsonReceive); // create polylines // Clustering if (clusteringEnabled && jsonReceive.Zoomlevel < AlgoConfig.ZoomlevelClusterStop) { // Calculate data to be displayed var clusterPoints = clusterAlgo.GetCluster(new ClusterInfo { ZoomLevel = jsonReceive.Zoomlevel, }); var converted = DataConvert(clusterPoints); // Prepare data to the client reply = new JsonMarkersReply { Markers = converted, Rid = sendid, Polylines = clusterAlgo.Lines, Msec = Sw(sw), }; // Return client data return(reply); } // If we are here then there are no clustering // The number of items returned is restricted to avoid json data overflow IPoints filteredDataset = ClusterAlgorithmBase.FilterDataset(points, jsonReceive.Viewport); IPoints filteredDatasetMaxPoints = new Points { Data = filteredDataset.Data .Take(AlgoConfig.MaxMarkersReturned) .ToList() }; reply = new JsonMarkersReply { Markers = DataConvert(filteredDatasetMaxPoints), Rid = sendid, Polylines = clusterAlgo.Lines, Mia = filteredDataset.Count - filteredDatasetMaxPoints.Count, Msec = Sw(sw), }; return(reply); }