|
1 | 1 | /* |
2 | | - * Copyright (c) 1998-2020 John Caron and University Corporation for Atmospheric Research/Unidata |
| 2 | + * Copyright (c) 1998-2025 John Caron and University Corporation for Atmospheric Research/Unidata |
3 | 3 | * See LICENSE for license information. |
4 | 4 | */ |
| 5 | + |
5 | 6 | package ucar.nc2.ft2.coverage.writer; |
6 | 7 |
|
7 | 8 | import com.google.common.base.Preconditions; |
|
13 | 14 | import java.util.List; |
14 | 15 | import java.util.Map; |
15 | 16 | import java.util.Optional; |
| 17 | +import java.util.stream.Collectors; |
16 | 18 | import javax.annotation.Nullable; |
17 | 19 | import ucar.ma2.Array; |
18 | 20 | import ucar.ma2.DataType; |
19 | 21 | import ucar.ma2.InvalidRangeException; |
20 | 22 | import ucar.ma2.Section; |
21 | 23 | import ucar.nc2.Attribute; |
22 | 24 | import ucar.nc2.AttributeContainer; |
| 25 | +import ucar.nc2.AttributeContainerMutable; |
23 | 26 | import ucar.nc2.Dimension; |
24 | 27 | import ucar.nc2.Group; |
25 | 28 | import ucar.nc2.Variable; |
|
28 | 31 | import ucar.nc2.constants.CDM; |
29 | 32 | import ucar.nc2.constants.CF; |
30 | 33 | import ucar.nc2.constants._Coordinate; |
| 34 | +import ucar.nc2.dataset.transform.AbstractTransformBuilder; |
31 | 35 | import ucar.nc2.ft2.coverage.Coverage; |
32 | 36 | import ucar.nc2.ft2.coverage.CoverageCollection; |
33 | 37 | import ucar.nc2.ft2.coverage.CoverageCoordAxis; |
@@ -308,8 +312,53 @@ private void addCoordTransforms(CoverageCollection subsetDataset, Group.Builder |
308 | 312 | // scalar coordinate transform variable - container for transform info |
309 | 313 | Variable.Builder ctv = Variable.builder().setName(ct.getName()).setDataType(DataType.INT); |
310 | 314 | group.addVariable(ctv); |
311 | | - ctv.addAttributes(ct.attributes()); |
| 315 | + AttributeContainer ctAttrs = ct.attributes(); |
| 316 | + |
| 317 | + AttributeContainerMutable newAttrs = AttributeContainerMutable.copyFrom(ctAttrs); |
| 318 | + // adjust false_easting/false_northing if needed |
| 319 | + // possibly needed if the subset dataset includes GeoX or GeoY axes, so first find those |
| 320 | + Map<AxisType, CoverageCoordAxis> mapCoordAxes = subsetDataset.getCoordAxes().stream() |
| 321 | + .filter(ca -> ca.getAxisType() == AxisType.GeoX || ca.getAxisType() == AxisType.GeoY) |
| 322 | + .collect(Collectors.toMap(CoverageCoordAxis::getAxisType, mca -> mca)); |
| 323 | + // if we found GeoX and/or GeoY axes, start checking |
| 324 | + if (!mapCoordAxes.isEmpty()) { |
| 325 | + boolean eastScaled = false; |
| 326 | + if (mapCoordAxes.containsKey(AxisType.GeoX)) { |
| 327 | + eastScaled = scaleFalseEastingNorthing(CF.FALSE_EASTING, ctAttrs, newAttrs, |
| 328 | + mapCoordAxes.get(AxisType.GeoX).attributes().findAttributeString(CF.UNITS, null)); |
| 329 | + } |
| 330 | + boolean northScaled = false; |
| 331 | + if (mapCoordAxes.containsKey(AxisType.GeoY)) { |
| 332 | + northScaled = scaleFalseEastingNorthing(CF.FALSE_NORTHING, ctAttrs, newAttrs, |
| 333 | + mapCoordAxes.get(AxisType.GeoY).attributes().findAttributeString(CF.UNITS, null)); |
| 334 | + } |
| 335 | + // do not propagate the unit attribute on the projection variable if we ensured the |
| 336 | + // units match, as this is state matchs CF (likely this attribute was added when creating |
| 337 | + // a coverage from the NetcdfDataset. |
| 338 | + if (!ctAttrs.findAttributeString(CF.UNITS, "").isEmpty()) { |
| 339 | + if (eastScaled || northScaled) { |
| 340 | + newAttrs.removeAttribute(CF.UNITS); |
| 341 | + } |
| 342 | + } |
| 343 | + } |
| 344 | + ctv.addAttributes(newAttrs.toImmutable()); |
| 345 | + } |
| 346 | + } |
| 347 | + |
| 348 | + private boolean scaleFalseEastingNorthing(String falseValueType, AttributeContainer cta, |
| 349 | + AttributeContainerMutable newCta, String caUnits) { |
| 350 | + double falseValue = cta.findAttributeDouble(falseValueType, Double.MIN_VALUE); |
| 351 | + boolean scaled = false; |
| 352 | + if (falseValue != Double.MIN_VALUE) { |
| 353 | + double scalef = AbstractTransformBuilder.getFalseEastingScaleFactor(caUnits); |
| 354 | + if (scalef != 1.0) { |
| 355 | + scaled = true; |
| 356 | + newCta.removeAttribute(falseValueType); |
| 357 | + // here we divide by scalef, to undo the scalef applied when creating the Coverage |
| 358 | + newCta.addAttribute(Attribute.builder(falseValueType).setNumericValue(falseValue / scalef, false).build()); |
| 359 | + } |
312 | 360 | } |
| 361 | + return scaled; |
313 | 362 | } |
314 | 363 |
|
315 | 364 | private void addLatLon2D(CoverageCollection subsetDataset, Group.Builder group) { |
|
0 commit comments