diff --git a/cdm/core/src/main/java/ucar/unidata/geoloc/projection/LatLonProjection.java b/cdm/core/src/main/java/ucar/unidata/geoloc/projection/LatLonProjection.java index 7b40bbd175..46ade27b46 100644 --- a/cdm/core/src/main/java/ucar/unidata/geoloc/projection/LatLonProjection.java +++ b/cdm/core/src/main/java/ucar/unidata/geoloc/projection/LatLonProjection.java @@ -1,7 +1,8 @@ /* - * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata + * Copyright (c) 1998-2026 University Corporation for Atmospheric Research/Unidata * See LICENSE for license information. */ + package ucar.unidata.geoloc.projection; import ucar.nc2.constants.CF; @@ -248,23 +249,59 @@ public double[][] latLonToProj(double[][] from, double[][] to, int latIndex, int /** - * Set the center of the Longitude range. It is normalized to +/- 180. + * Set the center longitude of this projection, normalizing it into the range [-180, 180]. + * + *

* The cylinder is cut at the "seam" = centerLon +- 180. * Use this to keep the Longitude values kept in the range [centerLon +-180], which * makes seam handling easier. + * {@link #latLonToProj} returns longitudes in the range + * [{@code centerLon} - 180, {@code centerLon} + 180], so the center longitude controls where the + * seam falls and therefore which longitude values are produced. + * + *

+ * This method always normalizes the supplied value to [-180, 180], which discards information + * about grids whose longitude coordinates legitimately lie outside that range. To preserve + * such information, use + * {@link #setCenterLon(double, boolean)} with {@code normalize = false}. * - * @param centerLon the center of the Longitude range. - * @return centerLon normalized to +/- 180. + * @param centerLon the center of the longitude range, in degrees east. + * @return centerLon normalized to [-180, 180]. + * @deprecated use {@link #setCenterLon(double, boolean)} with {@code normalize = true} */ + @Deprecated public double setCenterLon(double centerLon) { - this.centerLon = LatLonPoints.lonNormal(centerLon); + return setCenterLon(centerLon, true); + } + + /** + * Set the center longitude of this projection, optionally normalizing it into the range [-180, 180]. + * + *

+ * The center longitude controls the location of the projection "seam" (at {@code centerLon} +/- 180). + * This also means it controls the range of longitudes returned by the projection. When {@code normalize} + * is {@code false}, the supplied value is stored as given. This allows the projection to + * reproduce grid longitudes that lie outside [-180, 180]. + * + * @param centerLon the center of the longitude range, in degrees east. + * @param normalize if {@code true}, normalize {@code centerLon} into [-180, 180]; if {@code false}, + * store it unchanged. + * @return the center longitude that was stored. + */ + public double setCenterLon(double centerLon, boolean normalize) { + this.centerLon = normalize ? LatLonPoints.lonNormal(centerLon) : centerLon; return this.centerLon; } /** - * Get the center of the Longitude range. It is normalized to +/- 180. + * Get the center longitude of this projection, in degrees east. + * + *

+ * Note that this value is normalized to [-180, 180] only if it was set via + * {@link #setCenterLon(double)} (or {@link #setCenterLon(double, boolean)} with + * {@code normalize = true}). * - * @return the center longitude + * @return the center longitude. */ public double getCenterLon() { return centerLon; diff --git a/grib/src/main/java/ucar/nc2/grib/grib1/Grib1Gds.java b/grib/src/main/java/ucar/nc2/grib/grib1/Grib1Gds.java index 102f8dd7fa..0d688fd847 100644 --- a/grib/src/main/java/ucar/nc2/grib/grib1/Grib1Gds.java +++ b/grib/src/main/java/ucar/nc2/grib/grib1/Grib1Gds.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2025 John Caron and University Corporation for Atmospheric Research/Unidata + * Copyright (c) 1998-2026 John Caron and University Corporation for Atmospheric Research/Unidata * See LICENSE for license information. */ @@ -557,9 +557,7 @@ public String toString() { public GdsHorizCoordSys makeHorizCoordSys() { LatLonProjection proj = new LatLonProjection(getEarth()); double centerLon = ((int) ((lo2 - lo1 + deltaLon) / 2 / scale3)) * scale3; - proj.setCenterLon(centerLon); - - // ProjectionPoint startP = proj.latLonToProj(LatLonPoint.create(la1, lo1)); + proj.setCenterLon(centerLon, false); double startx = lo1; // startP.getX(); double starty = la1; // startP.getY(); return new GdsHorizCoordSys(getNameShort(), template, 0, scanMode, proj, startx, getDx(), starty, getDy(), diff --git a/grib/src/main/java/ucar/nc2/grib/grib2/Grib2Gds.java b/grib/src/main/java/ucar/nc2/grib/grib2/Grib2Gds.java index 3550412711..f2878c8499 100644 --- a/grib/src/main/java/ucar/nc2/grib/grib2/Grib2Gds.java +++ b/grib/src/main/java/ucar/nc2/grib/grib2/Grib2Gds.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2025 John Caron and University Corporation for Atmospheric Research/Unidata + * Copyright (c) 1998-2026 John Caron and University Corporation for Atmospheric Research/Unidata * See LICENSE for license information. */ @@ -519,8 +519,7 @@ public int[] getOptionalPoints() { public GdsHorizCoordSys makeHorizCoordSys() { LatLonProjection proj = new LatLonProjection(getEarth()); double centerLon = ((int) ((lo2 - lo1 + deltaLon) / 2 / getScale())) * getScale(); - proj.setCenterLon(centerLon); - // ProjectionPoint startP = proj.latLonToProj(LatLonPoint.create(la1, lo1)); + proj.setCenterLon(centerLon, false); double startx = lo1; // startP.getX(); double starty = la1; // startP.getY(); return new GdsHorizCoordSys(getNameShort(), template, numberOfDataPoints, scanMode, proj, startx, deltaLon,