Modulus to limit latitude and longitude values

There is a much, much more efficient way to do this than using sin and arcsin. The most expensive operation is a single division. The observation that the required interval is closed is key.

  • Divide by 360 and take the remainder. This yields a number in the interval [0, 360), which is half-open, as observed.

  • Fold the interval in half. If the remainder is >=180, subtract it from 360. This maps the interval [180, 360) to the interval (0, 180]. The union of this interval with the bottom half is the closed interval [0, 180].

  • Subtract 90 from the result. This interval is [-90, 90], as desired.

This is, indeed, the exact same function as arcsin(sin(x)), but without the expense or any issue with numeric stability.


Using trig functions sin()/cos() is expensive in time and introduces loss of precision. Much better to use the remainder() function. Note the result has the same sign as x and magnitude less than the magnitude of y, if able.

OP was on the right track! The below solution is easy to adjust per the edge values of -180 and + 180.0.

#include <math.h>

// Reduce to (-180.0, 180.0]
double Limit_Longitude(double longitude_degrees) {
  // A good implementation of `fmod()` will introduce _no_ loss of precision.
  // -360.0 <= longitude_reduced <=- 360.0
  double longitude_reduced = fmod(longitude_degrees, 360.0);

  if (longitude_reduced > 180.0) {
    longitude_reduced -= 360.0;
  } else if (longitude_reduced <= -180.0) {
    longitude_reduced += 360.0;
  }
  return longitude_reduced;
}

Limiting Latitude to [-90 to +90] is trickier as a latitude of +91 degrees is going over the North Pole but switching the longitude +/- 180 degrees. To preserve longitude precision, adjust by 180 toward 0 degrees.

void Limit_Latitude_Longitude(double *latitude_degrees, double *longitude_degrees) {
  *latitude_degrees = Limit_Longitude(*latitude_degrees);
  int flip = 0;
  if (*latitude_degrees > 90.0) {
    *latitude_degrees = 180.0 - *latitude_degrees;
    flip = 1;
  } else if (*latitude_degrees < -90.0) {
    *latitude_degrees = -180.0 - *latitude_degrees;
    flip = 1;
  }
  if (flip) {
    *longitude_degrees += *longitude_degrees > 0 ? -180.0 : 180.0;
  }
  *longitude_degrees = Limit_Longitude(*longitude_degrees);
}

Minor: Although the goal is "limit longitudes to (-180.0, 180.0]", I'd expect ranges of [-180.0, 180.0), [-180.0, 180.0] to be more commonly needed.


How about using the sin and inverse functions?

asin(sin((lat/180.0)*3.14159265)) * (180.0/3.14159265);