/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using Spatial4n.Core.Context; using Spatial4n.Core.Shapes; using Spatial4n.Core.Util; namespace Lucene.Net.Spatial.Prefix.Tree { /// /// A SpatialPrefixGrid based on Geohashes. Uses {@link GeohashUtils} to do all the geohash work. /// public class GeohashPrefixTree : SpatialPrefixTree { /// /// Factory for creating {@link GeohashPrefixTree} instances with useful defaults /// public class Factory : SpatialPrefixTreeFactory { protected override int GetLevelForDistance(double degrees) { var grid = new GeohashPrefixTree(ctx, GeohashPrefixTree.GetMaxLevelsPossible()); return grid.GetLevelForDistance(degrees); } protected override SpatialPrefixTree NewSPT() { return new GeohashPrefixTree(ctx, maxLevels != null ? maxLevels.Value : GeohashPrefixTree.GetMaxLevelsPossible()); } } public GeohashPrefixTree(SpatialContext ctx, int maxLevels) : base(ctx, maxLevels) { Rectangle bounds = ctx.GetWorldBounds(); if (bounds.GetMinX() != -180) throw new ArgumentException("Geohash only supports lat-lon world bounds. Got " + bounds); int MAXP = GetMaxLevelsPossible(); if (maxLevels <= 0 || maxLevels > MAXP) throw new ArgumentException("maxLen must be [1-" + MAXP + "] but got " + maxLevels); } /// /// Any more than this and there's no point (double lat and lon are the same). /// /// public static int GetMaxLevelsPossible() { return GeohashUtils.MAX_PRECISION; } public override int GetLevelForDistance(double dist) { if (dist == 0) return maxLevels;//short circuit int level = GeohashUtils.LookupHashLenForWidthHeight(dist, dist); return Math.Max(Math.Min(level, maxLevels), 1); } protected override Node GetNode(Point p, int level) { return new GhCell(GeohashUtils.EncodeLatLon(p.GetY(), p.GetX(), level), this);//args are lat,lon (y,x) } public override Node GetNode(string token) { return new GhCell(token, this); } public override Node GetNode(byte[] bytes, int offset, int len) { throw new System.NotImplementedException(); } public override IList GetNodes(Shape shape, int detailLevel, bool inclParents) { var s = shape as Point; return (s != null) ? base.GetNodesAltPoint(s, detailLevel, inclParents) : base.GetNodes(shape, detailLevel, inclParents); } public class GhCell : Node { public GhCell(String token, GeohashPrefixTree enclosingInstance) : base(enclosingInstance, token) { } public override void Reset(string newToken) { base.Reset(newToken); shape = null; } public override IList GetSubCells() { String[] hashes = GeohashUtils.GetSubGeohashes(GetGeohash());//sorted var cells = new List(hashes.Length); var enclosingInstance = (GeohashPrefixTree)spatialPrefixTree; foreach (String hash in hashes) { cells.Add(new GhCell(hash, enclosingInstance)); } return cells; } public override int GetSubCellsSize() { return 32;//8x4 } public override Node GetSubCell(Point p) { return ((GeohashPrefixTree)spatialPrefixTree).GetNode(p, GetLevel() + 1); //not performant! } private Shape shape;//cache public override Shape GetShape() { if (shape == null) { shape = GeohashUtils.DecodeBoundary(GetGeohash(), ((GeohashPrefixTree)spatialPrefixTree).ctx); } return shape; } public override Point GetCenter() { return GeohashUtils.Decode(GetGeohash(), ((GeohashPrefixTree)spatialPrefixTree).ctx); } private String GetGeohash() { return GetTokenString(); } }//class GhCell } }