/*
 * 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.
 */
package org.apache.lucene.spatial3d;

import org.apache.lucene.spatial3d.geom.PlanetModel;
import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
import org.apache.lucene.spatial3d.geom.GeoPathFactory;
import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
import org.apache.lucene.spatial3d.geom.GeoPath;
import org.apache.lucene.spatial3d.geom.GeoPolygon;
import org.apache.lucene.spatial3d.geom.GeoCircle;
import org.apache.lucene.spatial3d.geom.GeoBBox;
import org.apache.lucene.spatial3d.geom.GeoCompositePolygon;
import org.apache.lucene.spatial3d.geom.GeoPoint;

import org.apache.lucene.geo.Polygon;
import org.apache.lucene.geo.GeoUtils;

import java.util.List;
import java.util.ArrayList;

class Geo3DUtil {

  
How many radians are in one degree
/** How many radians are in one degree */
final static double RADIANS_PER_DEGREE = Math.PI / 180.0;
Returns smallest double that would encode to int x.
/** Returns smallest double that would encode to int x. */
// NOTE: remains in this class to keep method package private!! static double decodeValueFloor(int x, PlanetModel planetModel) { assert x <= planetModel.MAX_ENCODED_VALUE && x >= planetModel.MIN_ENCODED_VALUE; if (x == planetModel.MIN_ENCODED_VALUE) { return -planetModel.MAX_VALUE; } return x * planetModel.DECODE; }
Returns largest double that would encode to int x.
/** Returns largest double that would encode to int x. */
// NOTE: keep this package private!! static double decodeValueCeil(int x, PlanetModel planetModel) { assert x <= planetModel.MAX_ENCODED_VALUE && x >= planetModel.MIN_ENCODED_VALUE; if (x == planetModel.MAX_ENCODED_VALUE) { return planetModel.MAX_VALUE; } return Math.nextDown((x+1) * planetModel.DECODE); }
Converts degress to radians
/** Converts degress to radians */
static double fromDegrees(final double degrees) { return degrees * RADIANS_PER_DEGREE; }
Convert a set of Polygon objects into a GeoPolygon.
Params:
  • polygons – are the Polygon objects.
Returns:the GeoPolygon.
/** * Convert a set of Polygon objects into a GeoPolygon. * @param polygons are the Polygon objects. * @return the GeoPolygon. */
static GeoPolygon fromPolygon(final PlanetModel planetModel, final Polygon... polygons) { //System.err.println("Creating polygon..."); if (polygons.length < 1) { throw new IllegalArgumentException("need at least one polygon"); } final GeoPolygon shape; if (polygons.length == 1) { final GeoPolygon component = fromPolygon(planetModel, polygons[0]); if (component == null) { // Polygon is degenerate shape = new GeoCompositePolygon(planetModel); } else { shape = component; } } else { final GeoCompositePolygon poly = new GeoCompositePolygon(planetModel); for (final Polygon p : polygons) { final GeoPolygon component = fromPolygon(planetModel, p); if (component != null) { poly.addShape(component); } } shape = poly; } return shape; //System.err.println("...done"); }
Convert a Polygon object to a large GeoPolygon.
Params:
  • polygons – is the list of polygons to convert.
Returns:the large GeoPolygon.
/** * Convert a Polygon object to a large GeoPolygon. * @param polygons is the list of polygons to convert. * @return the large GeoPolygon. */
static GeoPolygon fromLargePolygon(final PlanetModel planetModel, final Polygon... polygons) { if (polygons.length < 1) { throw new IllegalArgumentException("need at least one polygon"); } return GeoPolygonFactory.makeLargeGeoPolygon(planetModel, convertToDescription(planetModel, polygons)); }
Convert input parameters to a path.
Params:
  • pathLatitudes – latitude values for points of the path: must be within standard +/-90 coordinate bounds.
  • pathLongitudes – longitude values for points of the path: must be within standard +/-180 coordinate bounds.
  • pathWidthMeters – width of the path in meters.
Returns:the path.
/** * Convert input parameters to a path. * @param pathLatitudes latitude values for points of the path: must be within standard +/-90 coordinate bounds. * @param pathLongitudes longitude values for points of the path: must be within standard +/-180 coordinate bounds. * @param pathWidthMeters width of the path in meters. * @return the path. */
static GeoPath fromPath(final PlanetModel planetModel, final double[] pathLatitudes, final double[] pathLongitudes, final double pathWidthMeters) { if (pathLatitudes.length != pathLongitudes.length) { throw new IllegalArgumentException("same number of latitudes and longitudes required"); } final GeoPoint[] points = new GeoPoint[pathLatitudes.length]; for (int i = 0; i < pathLatitudes.length; i++) { final double latitude = pathLatitudes[i]; final double longitude = pathLongitudes[i]; GeoUtils.checkLatitude(latitude); GeoUtils.checkLongitude(longitude); points[i] = new GeoPoint(planetModel, fromDegrees(latitude), fromDegrees(longitude)); } double radiusRadians = pathWidthMeters / (planetModel.getMeanRadius() * planetModel.xyScaling); return GeoPathFactory.makeGeoPath(planetModel, radiusRadians, points); }
Convert input parameters to a circle.
Params:
  • latitude – latitude at the center: must be within standard +/-90 coordinate bounds.
  • longitude – longitude at the center: must be within standard +/-180 coordinate bounds.
  • radiusMeters – maximum distance from the center in meters: must be non-negative and finite.
Returns:the circle.
/** * Convert input parameters to a circle. * @param latitude latitude at the center: must be within standard +/-90 coordinate bounds. * @param longitude longitude at the center: must be within standard +/-180 coordinate bounds. * @param radiusMeters maximum distance from the center in meters: must be non-negative and finite. * @return the circle. */
static GeoCircle fromDistance(final PlanetModel planetModel, final double latitude, final double longitude, final double radiusMeters) { GeoUtils.checkLatitude(latitude); GeoUtils.checkLongitude(longitude); double radiusRadians = radiusMeters / (planetModel.getMeanRadius()); return GeoCircleFactory.makeGeoCircle(planetModel, fromDegrees(latitude), fromDegrees(longitude), radiusRadians); }
Convert input parameters to a box.
Params:
  • minLatitude – latitude lower bound: must be within standard +/-90 coordinate bounds.
  • maxLatitude – latitude upper bound: must be within standard +/-90 coordinate bounds.
  • minLongitude – longitude lower bound: must be within standard +/-180 coordinate bounds.
  • maxLongitude – longitude upper bound: must be within standard +/-180 coordinate bounds.
Returns:the box.
/** * Convert input parameters to a box. * @param minLatitude latitude lower bound: must be within standard +/-90 coordinate bounds. * @param maxLatitude latitude upper bound: must be within standard +/-90 coordinate bounds. * @param minLongitude longitude lower bound: must be within standard +/-180 coordinate bounds. * @param maxLongitude longitude upper bound: must be within standard +/-180 coordinate bounds. * @return the box. */
static GeoBBox fromBox(final PlanetModel planetModel, final double minLatitude, final double maxLatitude, final double minLongitude, final double maxLongitude) { GeoUtils.checkLatitude(minLatitude); GeoUtils.checkLongitude(minLongitude); GeoUtils.checkLatitude(maxLatitude); GeoUtils.checkLongitude(maxLongitude); return GeoBBoxFactory.makeGeoBBox(planetModel, Geo3DUtil.fromDegrees(maxLatitude), Geo3DUtil.fromDegrees(minLatitude), Geo3DUtil.fromDegrees(minLongitude), Geo3DUtil.fromDegrees(maxLongitude)); }
Convert a Polygon object into a GeoPolygon. This method uses
Params:
  • polygon – is the Polygon object.
Returns:the GeoPolygon.
/** * Convert a Polygon object into a GeoPolygon. * This method uses * @param polygon is the Polygon object. * @return the GeoPolygon. */
private static GeoPolygon fromPolygon(final PlanetModel planetModel, final Polygon polygon) { // First, assemble the "holes". The geo3d convention is to use the same polygon sense on the inner ring as the // outer ring, so we process these recursively with reverseMe flipped. final Polygon[] theHoles = polygon.getHoles(); final List<GeoPolygon> holeList = new ArrayList<>(theHoles.length); for (final Polygon hole : theHoles) { //System.out.println("Hole: "+hole); final GeoPolygon component = fromPolygon(planetModel, hole); if (component != null) { holeList.add(component); } } // Now do the polygon itself final double[] polyLats = polygon.getPolyLats(); final double[] polyLons = polygon.getPolyLons(); // I presume the arguments have already been checked final List<GeoPoint> points = new ArrayList<>(polyLats.length-1); // We skip the last point anyway because the API requires it to be repeated, and geo3d doesn't repeat it. for (int i = 0; i < polyLats.length - 1; i++) { final int index = polyLats.length - 2 - i; points.add(new GeoPoint(planetModel, fromDegrees(polyLats[index]), fromDegrees(polyLons[index]))); } //System.err.println(" building polygon with "+points.size()+" points..."); final GeoPolygon rval = GeoPolygonFactory.makeGeoPolygon(planetModel, points, holeList); //System.err.println(" ...done"); return rval; }
Convert a list of polygons to a list of polygon descriptions.
Params:
  • polygons – is the list of polygons to convert.
Returns:the list of polygon descriptions.
/** * Convert a list of polygons to a list of polygon descriptions. * @param polygons is the list of polygons to convert. * @return the list of polygon descriptions. */
private static List<GeoPolygonFactory.PolygonDescription> convertToDescription(final PlanetModel planetModel, final Polygon... polygons) { final List<GeoPolygonFactory.PolygonDescription> descriptions = new ArrayList<>(polygons.length); for (final Polygon polygon : polygons) { final Polygon[] theHoles = polygon.getHoles(); final List<GeoPolygonFactory.PolygonDescription> holes = convertToDescription(planetModel, theHoles); // Now do the polygon itself final double[] polyLats = polygon.getPolyLats(); final double[] polyLons = polygon.getPolyLons(); // I presume the arguments have already been checked final List<GeoPoint> points = new ArrayList<>(polyLats.length-1); // We skip the last point anyway because the API requires it to be repeated, and geo3d doesn't repeat it. for (int i = 0; i < polyLats.length - 1; i++) { final int index = polyLats.length - 2 - i; points.add(new GeoPoint(planetModel, fromDegrees(polyLats[index]), fromDegrees(polyLons[index]))); } descriptions.add(new GeoPolygonFactory.PolygonDescription(points, holes)); } return descriptions; } }