/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch 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.elasticsearch.index.search.geo;
/**
*/
public class GeoUtils {
/**
* Normalize longitude to lie within the -180 (exclusive) to 180 (inclusive) range.
*
* @param lon Longitude to normalize
* @see #normalizePoint(Point)
* @return The normalized longitude.
*/
public static double normalizeLon(double lon) {
return centeredModulus(lon, 360);
}
/**
* Normalize latitude to lie within the -90 to 90 (both inclusive) range.
* <p>
* Note: You should not normalize longitude and latitude separately,
* because when normalizing latitude it may be necessary to
* add a shift of 180° in the longitude.
* For this purpose, you should call the
* {@link #normalizePoint(Point)} function.
*
* @param lat Latitude to normalize
* @see #normalizePoint(Point)
* @return The normalized latitude.
*/
public static double normalizeLat(double lat) {
lat = centeredModulus(lat, 360);
if (lat < -90) {
lat = -180 - lat;
} else if (lat > 90) {
lat = 180 - lat;
}
return lat;
}
/**
* Normalize the geo {@code Point} for its coordinates to lie within their
* respective normalized ranges.
* <p>
* Note: A shift of 180° is applied in the longitude if necessary,
* in order to normalize properly the latitude.
*
* @param point The point to normalize in-place.
*/
public static void normalizePoint(Point point) {
normalizePoint(point, true, true);
}
/**
* Normalize the geo {@code Point} for the given coordinates to lie within
* their respective normalized ranges.
*
* You can control which coordinate gets normalized with the two flags.
* <p>
* Note: A shift of 180° is applied in the longitude if necessary,
* in order to normalize properly the latitude.
* If normalizing latitude but not longitude, it is assumed that
* the longitude is in the form x+k*360, with x in ]-180;180],
* and k is meaningful to the application.
* Therefore x will be adjusted while keeping k preserved.
*
* @param point The point to normalize in-place.
* @param normLat Whether to normalize latitude or leave it as is.
* @param normLon Whether to normalize longitude.
*/
public static void normalizePoint(Point point, boolean normLat, boolean normLon) {
if (normLat) {
point.lat = centeredModulus(point.lat, 360);
boolean shift = true;
if (point.lat < -90) {
point.lat = -180 - point.lat;
} else if (point.lat > 90) {
point.lat = 180 - point.lat;
} else {
// No need to shift the longitude, and the latitude is normalized
shift = false;
}
if (shift) {
if (normLon) {
point.lon += 180;
} else {
// Longitude won't be normalized,
// keep it in the form x+k*360 (with x in ]-180;180])
// by only changing x, assuming k is meaningful for the user application.
point.lon += normalizeLon(point.lon) > 0 ? -180 : 180;
}
}
}
if (normLon) {
point.lon = centeredModulus(point.lon, 360);
}
}
private static double centeredModulus(double dividend, double divisor) {
double rtn = dividend % divisor;
if (rtn <= 0) {
rtn += divisor;
}
if (rtn > divisor / 2) {
rtn -= divisor;
}
return rtn;
}
}