Skip to content

Commit d8bf451

Browse files
authored
Merge pull request #142 from berkgeveci/fix/map-origin-window
fix(origin): correct the map window at +180 and after rotating back
2 parents 43deb3d + ce9e2c1 commit d8bf451

1 file changed

Lines changed: 71 additions & 32 deletions

File tree

‎src/e3sm_quickview/plugins/eam_projection.py‎

Lines changed: 71 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,12 @@ def __init__(self):
694694
self.lon_range = [-180.0, 180.0]
695695
self.lat_range = [-90.0, 90.0]
696696
self.cached_cell_centers = None
697+
self._cached_geometry_key = None
698+
#: Strong ref to the geometry the caches were built from, so nothing
699+
#: else can be allocated at the same address while its id() is a key.
700+
self._cached_geometry = None
697701
self._cached_output = None
702+
self._cached_crop_key = None
698703
self._last_was_cropped = False
699704

700705
def SetLongitudeRange(self, min, max):
@@ -734,8 +739,25 @@ def RequestData(self, request, inInfo, outInfo):
734739
self._last_was_cropped = False
735740
return 1
736741

737-
if self.cached_cell_centers and self.cached_cell_centers.GetMTime() >= max(
738-
inData.GetPoints().GetMTime(), inData.GetCells().GetMTime()
742+
# Name the incoming geometry by identity as well as by modified
743+
# time. Modified times only order events within one object, and
744+
# upstream can legitimately hand back an *older* object than the
745+
# one these caches were built from: EAMCenterMeridian passes the
746+
# reader's own points straight through whenever the map already
747+
# sits in the reader's window. Comparing times alone then made a
748+
# cache built from a rotated mesh look fresh, and the cell centres
749+
# of one mesh were used to mask the cells of another.
750+
in_points = inData.GetPoints()
751+
in_cells = inData.GetCells()
752+
geometry_key = (
753+
id(in_points),
754+
in_points.GetMTime(),
755+
id(in_cells),
756+
in_cells.GetMTime(),
757+
)
758+
if (
759+
self.cached_cell_centers is not None
760+
and self._cached_geometry_key == geometry_key
739761
):
740762
cell_centers = self.cached_cell_centers
741763
else:
@@ -752,13 +774,14 @@ def RequestData(self, request, inInfo, outInfo):
752774
# previous cached_cell_centers, if any,
753775
# is available for garbage collection after this assignment
754776
self.cached_cell_centers = cell_centers
777+
self._cached_geometry_key = geometry_key
778+
self._cached_geometry = (in_points, in_cells)
755779

756780
# get the numpy array for cell centers
757781
cc = numpy_support.vtk_to_numpy(cell_centers)
758782

759-
if self._cached_output and self._cached_output.GetMTime() >= max(
760-
self.GetMTime(), inData.GetPoints().GetMTime(), cell_centers.GetMTime()
761-
):
783+
crop_key = (geometry_key, tuple(self.lon_range), tuple(self.lat_range))
784+
if self._cached_output is not None and self._cached_crop_key == crop_key:
762785
with _perf.timed("extract.cache_hit"):
763786
add_cell_and_point_arrays(inData, outData, self._cached_output)
764787
else:
@@ -816,6 +839,7 @@ def RequestData(self, request, inInfo, outInfo):
816839

817840
self._cached_output = outData.NewInstance()
818841
self._cached_output.ShallowCopy(outData)
842+
self._cached_crop_key = crop_key
819843
self._last_was_cropped = True
820844
return 1
821845

@@ -885,8 +909,10 @@ def SetMeridian(self, meridian_):
885909
)
886910
)
887911
return
888-
self._center_meridian = meridian_
889-
self.Modified()
912+
if self._center_meridian != meridian_:
913+
self._center_meridian = meridian_
914+
self._cached_output = None
915+
self.Modified()
890916

891917
def SetLongitudeOrigin(self, origin):
892918
"""Left edge of the map; the right edge is 360 degrees further east."""
@@ -931,9 +957,13 @@ def RequestData(self, request, inInfo, outInfo):
931957
# Nothing to do when the input already sits in the requested
932958
# window -- the dycore reader's default case. Clipping here would
933959
# be a no-op that still rebuilds the points every pass, which also
934-
# costs EAMProject its cache downstream.
960+
# costs EAMProject its cache downstream. The two origins have to
961+
# agree exactly: a window a whole turn away covers the same
962+
# meridians but names them 360 degrees apart, and everything
963+
# downstream -- the crop ranges, the projection's re-centring --
964+
# reads coordinates, not residues.
935965
origin = self._center_meridian - 180.0
936-
if (origin - self._input_origin) % 360.0 == 0.0:
966+
if origin == self._input_origin:
937967
with _perf.timed("center_meridian.passthrough"):
938968
outData.ShallowCopy(inData)
939969
return 1
@@ -960,30 +990,39 @@ def RequestData(self, request, inInfo, outInfo):
960990
generate_ids.SetCellIdsArrayName("PedigreeIds")
961991

962992
cut, shift_low, shift_high = _longitude_window(
963-
self._center_meridian - 180.0, self._input_origin
993+
origin, self._input_origin
964994
)
965-
plane = vtkPlane()
966-
plane.SetOrigin([cut, 0.0, 0.0])
967-
plane.SetNormal([-1, 0, 0])
968-
# vtkClipPolyData hangs
969-
clipL = vtkTableBasedClipDataSet()
970-
clipL.SetClipFunction(plane)
971-
clipL.SetInputConnection(generate_ids.GetOutputPort())
972-
with _perf.timed("center_meridian.clip_left"):
973-
clipL.Update()
974-
975-
plane.SetNormal([1, 0, 0])
976-
clipR = vtkTableBasedClipDataSet()
977-
clipR.SetClipFunction(plane)
978-
clipR.SetInputConnection(generate_ids.GetOutputPort())
979-
with _perf.timed("center_meridian.clip_right"):
980-
clipR.Update()
981-
982-
with _perf.timed("center_meridian.transform"):
983-
halves = [
984-
_translated(clipL.GetOutput(), shift_low),
985-
_translated(clipR.GetOutput(), shift_high),
986-
]
995+
if (origin - self._input_origin) % 360.0 == 0.0:
996+
# A whole turn away: the cut falls on the window's own
997+
# edge, so one side comes back empty and there is
998+
# nothing to rearrange. Sliding the whole mesh over is
999+
# cheaper than clipping it and keeps every cell intact.
1000+
generate_ids.Update()
1001+
with _perf.timed("center_meridian.transform"):
1002+
halves = [_translated(generate_ids.GetOutput(), shift_high)]
1003+
else:
1004+
plane = vtkPlane()
1005+
plane.SetOrigin([cut, 0.0, 0.0])
1006+
plane.SetNormal([-1, 0, 0])
1007+
# vtkClipPolyData hangs
1008+
clipL = vtkTableBasedClipDataSet()
1009+
clipL.SetClipFunction(plane)
1010+
clipL.SetInputConnection(generate_ids.GetOutputPort())
1011+
with _perf.timed("center_meridian.clip_left"):
1012+
clipL.Update()
1013+
1014+
plane.SetNormal([1, 0, 0])
1015+
clipR = vtkTableBasedClipDataSet()
1016+
clipR.SetClipFunction(plane)
1017+
clipR.SetInputConnection(generate_ids.GetOutputPort())
1018+
with _perf.timed("center_meridian.clip_right"):
1019+
clipR.Update()
1020+
1021+
with _perf.timed("center_meridian.transform"):
1022+
halves = [
1023+
_translated(clipL.GetOutput(), shift_low),
1024+
_translated(clipR.GetOutput(), shift_high),
1025+
]
9871026

9881027
append = vtkAppendFilter()
9891028
for half in halves:

0 commit comments

Comments
 (0)