/// <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
                });
            }
        }
Example #6
0
        /// <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);
        }