/* * 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; namespace Lucene.Net.Spatial.Tier.Projectors { public class CartesianTierPlotter { public const String DefaltFieldPrefix = "_tier_"; private readonly int _tierLevel; private int _tierLength; private int _tierBoxes; private readonly IProjector _projector; private readonly string _fieldPrefix; private const double Idd = 180d; public CartesianTierPlotter(int tierLevel, IProjector projector, string fieldPrefix) { _tierLevel = tierLevel; _fieldPrefix = fieldPrefix; _projector = projector; SetTierLength(); SetTierBoxes(); SetTierVerticalPosDivider(); } public int TierVerticalPosDivider{ get; private set; } private void SetTierLength() { _tierLength = (int)Math.Pow(2, _tierLevel); } private void SetTierBoxes() { _tierBoxes = (int)Math.Pow(_tierLength, 2); } private void SetTierVerticalPosDivider() { // ceiling of log base 10 of tierLen TierVerticalPosDivider = Convert.ToInt32(Math.Ceiling(Math.Log10(_tierLength))); // TierVerticalPosDivider = (int)Math.Pow(10, TierVerticalPosDivider); } /// /// TierBoxId is latitude box id + longitude box id /// where latitude box id, and longitude box id are transposed in to position /// coordinates. /// /// The latitude. /// The longitude. /// public double GetTierBoxId(double latitude, double longitude) { double[] coords = _projector.Coords(latitude, longitude); double id = GetBoxId(coords[0]) + (GetBoxId(coords[1]) / TierVerticalPosDivider); return id; } private double GetBoxId(double coord) { return Math.Floor(coord / (Idd / _tierLength)); } private double GetBoxId(double coord, int tierLen) { return Math.Floor(coord / (Idd / tierLen)); } /// /// Get the string name representing current tier _localTier<tiedId> /// public String GetTierFieldName() { return _fieldPrefix + _tierLevel; } /// /// Get the string name representing tierId _localTier<tierId> /// /// The tier id. public string GetTierFieldName(int tierId) { return _fieldPrefix + tierId; } /// /// Find the tier with the best fit for a bounding box /// Best fit is defined as the ceiling of /// log2 (circumference of earth / distance) /// distance is defined as the smallest box fitting /// the corner between a radius and a bounding box. /// /// Distances less than a mile return 15, finer granularity is /// in accurate /// /// The miles. /// public int BestFit(double miles) { //28,892 a rough circumference of the earth const int circ = 28892; double r = miles / 2.0; double corner = r - Math.Sqrt(Math.Pow(r, 2) / 2.0d); double times = circ / corner; int bestFit = (int)Math.Ceiling(Log2(times)) + 1; if (bestFit > 15) { // 15 is the granularity of about 1 mile // finer granularity isn't accurate with standard java math return 15; } return bestFit; } /// /// A log to the base 2 formula. /// Math.Log(value) / Math.Log(2) /// /// The value. public double Log2(double value) { return Math.Log(value) / Math.Log(2); } } }