// Get
        public JsonMarkersReply GetMarkers(string s)
        {
            var invalid = new JsonMarkersReply { Ok = "0" };

            if (string.IsNullOrWhiteSpace(s))
            {
                invalid.EMsg = "params is empty";
                return invalid;
            }

            var arr = s.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries);
            if (arr.Length < 5)
            {
                invalid.EMsg = "params length incorrect";
                return invalid;
            }

            var nvc = new NameValueCollection();
            foreach (var a in arr)
            {
                var kv = a.Split(new[] {"="}, StringSplitOptions.RemoveEmptyEntries);
                if (kv.Length != 2) continue;

                nvc.Add(kv[0], kv[1]);
            }

            foreach (var key in Ajax.MarkersReq)
            {
                if (nvc[key]!=null) continue;

                invalid.EMsg = string.Format("param {0} is missing",key);
                return invalid;
            }

            try
            {
                var nelat = nvc[Ajax.nelat].Replace("_", ".").ToDouble();
                var nelon = nvc[Ajax.nelon].Replace("_", ".").ToDouble();
                var swlat = nvc[Ajax.swlat].Replace("_", ".").ToDouble();
                var swlon = nvc[Ajax.swlon].Replace("_", ".").ToDouble();
                var zoomlevel = int.Parse(nvc[Ajax.zoom]);

                var filter = nvc[Ajax.filter] ?? "";
                var sendid = nvc[Ajax.sid] == null ? 1 : int.Parse(nvc[Ajax.sid]);

                // values are validated there
                return Markers(nelat, nelon, swlat, swlon, zoomlevel,
                    filter, sendid);
            }
            catch (Exception ex)
            {
                invalid.EMsg = string.Format("Parsing error param: {0}",
                    ex.Message);
            }

            return invalid;
        }
        // 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;
        }