def test_reverse_distance_traveled(self):

        s = Shoreline(type='reverse')

        starting = Location4D(latitude=39.05, longitude=-75.34, depth=0)
        ending = Location4D(latitude=38.96, longitude=-75.315, depth=0)

        difference = AsaGreatCircle.great_distance(start_point=starting,
                                                   end_point=ending)
        angle = AsaMath.azimuth_to_math_angle(azimuth=difference['azimuth'])
        distance = difference['distance']

        intersection = s.intersect(start_point=starting.point,
                                   end_point=ending.point)
        int4d = Location4D(point=intersection['point'])

        final_point = s.react(start_point=starting,
                              hit_point=int4d,
                              end_point=ending,
                              feature=intersection['feature'],
                              distance=distance,
                              angle=angle,
                              azimuth=difference['azimuth'],
                              reverse_azimuth=difference['reverse_azimuth'],
                              reverse_distance=0.000001)

        # Resulting point should be VERY close to the hit point.
        assert abs(int4d.latitude - final_point.latitude) < 0.005
        assert abs(int4d.longitude - final_point.longitude) < 0.005
    def test_reverse_12_times_then_start_point(self):

        s = Shoreline(type='reverse')

        starting = Location4D(latitude=39.05, longitude=-75.34, depth=0)
        ending = Location4D(latitude=38.96, longitude=-75.315, depth=0)

        difference = AsaGreatCircle.great_distance(start_point=starting,
                                                   end_point=ending)
        angle = AsaMath.azimuth_to_math_angle(azimuth=difference['azimuth'])
        distance = difference['distance']

        intersection = s.intersect(start_point=starting.point,
                                   end_point=ending.point)
        int4d = Location4D(point=intersection['point'])

        final_point = s.react(start_point=starting,
                              hit_point=int4d,
                              end_point=ending,
                              feature=intersection['feature'],
                              distance=distance,
                              angle=angle,
                              azimuth=difference['azimuth'],
                              reverse_azimuth=difference['reverse_azimuth'],
                              reverse_distance=9999999999999999999999999999)

        # Should be start location
        assert final_point.longitude == starting.longitude
        assert final_point.latitude == starting.latitude
        assert final_point.depth == starting.depth
    def test_reverse_left(self):

        s = Shoreline(type='reverse')

        starting = Location4D(latitude=39.1, longitude=-74.91, depth=0)
        ending = Location4D(latitude=39.1, longitude=-74.85, depth=0)

        difference = AsaGreatCircle.great_distance(start_point=starting,
                                                   end_point=ending)
        angle = AsaMath.azimuth_to_math_angle(azimuth=difference['azimuth'])
        distance = difference['distance']

        intersection = s.intersect(start_point=starting.point,
                                   end_point=ending.point)
        int4d = Location4D(point=intersection['point'])

        final_point = s.react(start_point=starting,
                              hit_point=int4d,
                              end_point=ending,
                              feature=intersection['feature'],
                              distance=distance,
                              angle=angle,
                              azimuth=difference['azimuth'],
                              reverse_azimuth=difference['reverse_azimuth'])

        # Since we are on a stright horizonal line, the latitude will change only slightly
        assert abs(final_point.latitude - starting.latitude) < 0.005

        # Resulting longitude should be between the startpoint and the intersection point
        assert final_point.longitude < int4d.longitude
        assert final_point.longitude > starting.longitude
    def test_reverse_up_left(self):

        s = Shoreline(type='reverse')

        starting = Location4D(latitude=39.05, longitude=-75.34, depth=0)
        ending = Location4D(latitude=38.96, longitude=-75.315, depth=0)

        difference = AsaGreatCircle.great_distance(start_point=starting,
                                                   end_point=ending)
        angle = AsaMath.azimuth_to_math_angle(azimuth=difference['azimuth'])
        distance = difference['distance']

        intersection = s.intersect(start_point=starting.point,
                                   end_point=ending.point)
        int4d = Location4D(point=intersection['point'])

        final_point = s.react(start_point=starting,
                              hit_point=int4d,
                              end_point=ending,
                              feature=intersection['feature'],
                              distance=distance,
                              angle=angle,
                              azimuth=difference['azimuth'],
                              reverse_azimuth=difference['reverse_azimuth'])

        # Resulting latitude should be between the startpoint and the intersection point
        assert final_point.latitude > int4d.latitude
        assert final_point.latitude < starting.latitude

        # Resulting longitude should be between the startpoint and the intersection point
        assert final_point.longitude < int4d.longitude
        assert final_point.longitude > starting.longitude
    def test_reverse_half_distance_until_in_water(self):

        s = Shoreline(type='reverse')

        starting = Location4D(latitude=39.05, longitude=-75.34, depth=0)
        ending = Location4D(latitude=38.96, longitude=-75.315, depth=0)

        difference = AsaGreatCircle.great_distance(start_point=starting,
                                                   end_point=ending)
        angle = AsaMath.azimuth_to_math_angle(azimuth=difference['azimuth'])
        distance = difference['distance']

        intersection = s.intersect(start_point=starting.point,
                                   end_point=ending.point)
        int4d = Location4D(point=intersection['point'])

        final_point = s.react(start_point=starting,
                              hit_point=int4d,
                              end_point=ending,
                              feature=intersection['feature'],
                              distance=distance,
                              angle=angle,
                              azimuth=difference['azimuth'],
                              reverse_azimuth=difference['reverse_azimuth'],
                              reverse_distance=40000)

        # Should be in water
        assert s.intersect(start_point=final_point.point,
                           end_point=final_point.point) is None
    def test_reindexing(self):

        p = Point(-73.745631, 40.336791)
        p2 = Point(-78.745631, 44.336791)
        p3 = Point(0, 0)
        st = time.time()
        s = Shoreline(point=p, spatialbuffer=2)
        s.index(point=p2, spatialbuffer=2)
        s.index(point=p3, spatialbuffer=2)
        print "Reindexing Time: " + str(time.time() - st)
    def test_multipart_shape_reindexing(self):

        p = Point(-73.745631, 40.336791)
        p2 = Point(-78.745631, 44.336791)
        p3 = Point(0, 0)
        st = time.time()
        shore_path = os.path.join(self.shoreline_path, "westcoast",
                                  "New_Land_Clean.shp")
        s = Shoreline(file=shore_path, point=p, spatialbuffer=1)
        s.index(point=p2, spatialbuffer=0.25)
        s.index(point=p3, spatialbuffer=0.25)
        print "Multipart Shoreline Reindexing Time: " + str(time.time() - st)
    def test_large_shape_reindexing(self):

        p = Point(-73.745631, 40.336791)
        p2 = Point(-78.745631, 44.336791)
        p3 = Point(0, 0)
        st = time.time()
        shore_path = os.path.join(self.shoreline_path, "alaska",
                                  "AK_Land_Basemap.shp")
        s = Shoreline(file=shore_path, point=p, spatialbuffer=0.25)
        s.index(point=p2, spatialbuffer=0.25)
        s.index(point=p3, spatialbuffer=0.25)
        print "Large Shoreline Reindexing Time: " + str(time.time() - st)
    def test_land_start_land_end_intersection(self):
        # Starts on land and ends on land
        s = Shoreline()

        # -75, 39.4 is on land
        # -75, 39.5 is on land
        starting = Location4D(latitude=39.4, longitude=-75, depth=0).point
        ending = Location4D(latitude=39.5, longitude=-75, depth=0).point

        self.assertRaises(Exception,
                          s.intersect,
                          start_point=starting,
                          end_point=ending)
    def test_land_start_water_end_intersection(self):
        # Starts on land and ends in the water
        s = Shoreline()

        # -75, 39.5 is on land
        # -75, 39   is in the middle of the Delaware Bay
        starting = Location4D(latitude=39.5, longitude=-75, depth=0).point
        ending = Location4D(latitude=39, longitude=-75, depth=0).point

        self.assertRaises(Exception,
                          s.intersect,
                          start_point=starting,
                          end_point=ending)
    def test_water_start_land_end_intersection(self):
        # Starts in the water and ends on land
        s = Shoreline()

        # -75, 39   is in the middle of the Delaware Bay
        # -75, 39.5 is on land
        # Intersection should be a Point starting somewhere around -75, 39.185 -> 39.195
        starting = Location4D(latitude=39, longitude=-75, depth=0).point
        ending = Location4D(latitude=39.5, longitude=-75, depth=0).point

        intersection = Location4D(
            point=s.intersect(start_point=starting, end_point=ending)['point'])
        assert -75 == intersection.longitude
        assert intersection.latitude > 39.185
        assert intersection.latitude < 39.195
    def set_geometry(self, geo):
        # If polygon is passed in, we need to trim it by the coastline
        # so we don't start particles on land
        if isinstance(geo, Polygon) and self._use_shoreline:
            c = geo.centroid
            b = geo.bounds
            spatialbuffer = max(b[2] - b[0], b[3] - b[1])
            shore_geoms = Shoreline(file=self.shoreline_path,
                                    point=c,
                                    spatialbuffer=spatialbuffer).geoms
            if len(shore_geoms) > 0:
                all_shore = cascaded_union(shore_geoms)
                geo = geo.difference(all_shore)

        self._geometry = geo
    def test_multipart_shape_intersection_speed(self):

        # Intersects on the west coast of NovaScotia

        starting = Location4D(longitude=-146.62, latitude=60.755,
                              depth=0).point
        ending = Location4D(longitude=-146.60, latitude=60.74, depth=0).point
        shore_path = os.path.join(self.shoreline_path, "westcoast",
                                  "New_Land_Clean.shp")
        s = Shoreline(file=shore_path, point=starting, spatialbuffer=1)

        st = time.time()
        intersection = s.intersect(start_point=starting,
                                   end_point=ending)['point']
        print "Multipart Shoreline Intersection Time: " + str(time.time() - st)
    def test_large_shape_intersection_speed(self):

        # Intersects on the west coast of NovaScotia

        starting = Location4D(longitude=-146.62, latitude=60.755,
                              depth=0).point
        ending = Location4D(longitude=-146.60, latitude=60.74, depth=0).point
        shore_path = os.path.join(self.shoreline_path, "alaska",
                                  "AK_Land_Basemap.shp")
        s = Shoreline(file=shore_path, point=starting, spatialbuffer=0.25)

        st = time.time()
        intersection = s.intersect(start_point=starting,
                                   end_point=ending)['point']
        print "Large Shoreline Intersection Time: " + str(time.time() - st)
    def test_intersection_speed(self):

        # Intersects on the west coast of NovaScotia

        starting = Location4D(longitude=-66.1842219282406177,
                              latitude=44.0141581697495852,
                              depth=0).point
        ending = Location4D(longitude=-66.1555195384399326,
                            latitude=44.0387992322117370,
                            depth=0).point
        s = Shoreline(point=starting, spatialbuffer=1)

        st = time.time()
        intersection = s.intersect(start_point=starting,
                                   end_point=ending)['point']
        print "Intersection Time: " + str(time.time() - st)
    def test_water_start_water_end_jump_over_land_intersection(self):
        # Starts on water and ends on water, but there is land inbetween
        s = Shoreline()

        # -75, 39   is in the middle of the Delaware Bay
        # -74, 39   is in the Atlantic
        # This jumps over a peninsula.
        # Intersection should be the Point -74.96 -> -74.94, 39
        #
        starting = Location4D(latitude=39, longitude=-75, depth=0).point
        ending = Location4D(latitude=39, longitude=-74, depth=0).point

        intersection = Location4D(
            point=s.intersect(start_point=starting, end_point=ending)['point'])

        assert 39 == intersection.latitude
        assert intersection.longitude > -74.96
        assert intersection.longitude < -74.94
Esempio n. 17
0
    def run(self):

        self.load_initial_dataset()

        redis_connection = None
        if self.redis_url is not None and self.redis_results_channel is not None:
            import redis
            redis_connection = redis.from_url(self.redis_url)

        # Setup shoreline
        self._shoreline = None
        if self.useshore is True:
            self._shoreline = Shoreline(
                path=self.shoreline_path,
                feature_name=self.shoreline_feature,
                point=self.release_location_centroid,
                spatialbuffer=self.shoreline_index_buffer)
            # Make sure we are not starting on land.  Raises exception if we are.
            self._shoreline.intersect(
                start_point=self.release_location_centroid,
                end_point=self.release_location_centroid)

        # Setup Bathymetry
        if self.usebathy is True:
            try:
                self._bathymetry = Bathymetry(file=self.bathy_path)
            except Exception:
                logger.exception(
                    "Could not load Bathymetry file: %s, using no Bathymetry for this run!"
                    % self.bathy_path)
                self.usebathy = False

        # Calculate datetime at every timestep
        modelTimestep, newtimes = AsaTransport.get_time_objects_from_model_timesteps(
            self.times, start=self.start_time)

        if self.time_method == 'interp':
            time_indexs = self.timevar.nearest_index(newtimes, select='before')
        elif self.time_method == 'nearest':
            time_indexs = self.timevar.nearest_index(newtimes)
        else:
            logger.warn("Method for computing u,v,w,temp,salt not supported!")
        try:
            assert len(newtimes) == len(time_indexs)
        except AssertionError:
            logger.exception(
                "Time indexes are messed up. Need to have equal datetime and time indexes"
            )
            raise

        # Keep track of how much time we spend in each area.
        tot_boundary_time = 0.
        tot_model_time = {}
        tot_read_data = 0.
        for m in self.models:
            tot_model_time[m.name] = 0.

        # Set the base conditions
        # If using Redis, send the results
        if redis_connection is not None:
            redis_connection.publish(self.redis_results_channel,
                                     json.dumps(self.particle.timestep_dump()))

        # loop over timesteps
        # We don't loop over the last time_index because
        # we need to query in the time_index and set the particle's
        # location as the 'newtime' object.
        for loop_i, i in enumerate(time_indexs[0:-1]):

            if self.active and self.active.value is False:
                raise ValueError("Particle exiting due to Failure.")

            newloc = None

            st = time.clock()
            # Get the variable data required by the models
            if self.time_method == 'nearest':
                u, v, w, temp, salt = self.get_nearest_data(i)
            elif self.time_method == 'interp':
                u, v, w, temp, salt = self.get_linterp_data(
                    i, newtimes[loop_i])
            else:
                logger.warn(
                    "Method for computing u,v,w,temp,salt is unknown. Only 'nearest' and 'interp' are supported."
                )
            tot_read_data += (time.clock() - st)

            # Get the bathy value at the particles location
            if self.usebathy is True:
                bathymetry_value = self._bathymetry.get_depth(
                    self.particle.location)
            else:
                bathymetry_value = -999999999999999

            # Age the particle by the modelTimestep (seconds)
            # 'Age' meaning the amount of time it has been forced.
            self.particle.age(seconds=modelTimestep[loop_i])

            # loop over models - sort these in the order you want them to run
            for model in self.models:
                st = time.clock()
                movement = model.move(self.particle,
                                      u,
                                      v,
                                      w,
                                      modelTimestep[loop_i],
                                      temperature=temp,
                                      salinity=salt,
                                      bathymetry_value=bathymetry_value)
                newloc = Location4D(latitude=movement['latitude'],
                                    longitude=movement['longitude'],
                                    depth=movement['depth'],
                                    time=newtimes[loop_i + 1])
                tot_model_time[m.name] += (time.clock() - st)
                if logger.isEnabledFor(logging.DEBUG):
                    logger.debug(
                        "%s - moved %.3f meters (horizontally) and %.3f meters (vertically) by %s with data from %s"
                        % (self.particle.logstring(), movement['distance'],
                           movement['vertical_distance'],
                           model.__class__.__name__,
                           newtimes[loop_i].isoformat()))
                if newloc:
                    st = time.clock()
                    self.boundary_interaction(
                        particle=self.particle,
                        starting=self.particle.location,
                        ending=newloc,
                        distance=movement['distance'],
                        angle=movement['angle'],
                        azimuth=movement['azimuth'],
                        reverse_azimuth=movement['reverse_azimuth'],
                        vertical_distance=movement['vertical_distance'],
                        vertical_angle=movement['vertical_angle'])
                    tot_boundary_time += (time.clock() - st)
                if logger.isEnabledFor(logging.DEBUG):
                    logger.debug(
                        "%s - was forced by %s and is now at %s" %
                        (self.particle.logstring(), model.__class__.__name__,
                         self.particle.location.logstring()))

            self.particle.note = self.particle.outputstring()
            # Each timestep, save the particles status and environmental variables.
            # This keep fields such as temp, salt, halted, settled, and dead matched up with the number of timesteps
            self.particle.save()

            # If using Redis, send the results
            if redis_connection is not None:
                redis_connection.publish(
                    self.redis_results_channel,
                    json.dumps(self.particle.timestep_dump()))

        self.dataset.closenc()

        # We won't pull data for the last entry in locations, but we need to populate it with fill data.
        self.particle.fill_gap()

        if self.usebathy is True:
            self._bathymetry.close()

        if self.useshore is True:
            self._shoreline.close()

        logger.info(
            textwrap.dedent('''Particle %i Stats:
                          Data read: %f seconds
                          Model forcing: %s seconds
                          Boundary intersection: %f seconds''' %
                            (self.particle.uid, tot_read_data, {
                                s: '{:g} seconds'.format(f)
                                for s, f in list(tot_model_time.items())
                            }, tot_boundary_time)))

        return self.particle
Esempio n. 18
0
    def plot_animate(self,
                     output,
                     temp_folder=None,
                     view=(45, -75),
                     bathy=os.path.join(
                         __file__,
                         "../../resources/bathymetry/ETOPO1_Bed_g_gmt4.grd"),
                     frame_prefix='_paegan',
                     extent=None,
                     stride=None,
                     shore_path=None):

        try:
            os.mkdirs(os.path.dirname(output))
        except:
            pass

        if not os.path.exists(os.path.dirname(output)):
            raise ValueError("Cannot create output directory")

        if temp_folder == None:
            temp_folder = os.path.dirname(output)

        if extent == None:
            visual_bbox = (self.nc.variables['lon'][:, 0].min() - .6,
                           self.nc.variables['lat'][:, 0].min() - .75,
                           self.nc.variables['lon'][:, 0].max() + .6,
                           self.nc.variables['lat'][:, 0].max() + .75
                           )  #tracks.buffer(1).bounds
        else:
            visual_bbox = extent

        pt = Point(((visual_bbox[2] - visual_bbox[0]) / 2) + visual_bbox[0],
                   ((visual_bbox[3] - visual_bbox[1]) / 2) + visual_bbox[1])
        coast_line = Shoreline(file=shore_path, point=pt,
                               spatialbuffer=1.5).linestring
        c_lons, c_lats = coast_line.xy
        c_lons = np.array(c_lons)
        c_lats = np.array(c_lats)
        c_lons = np.where(
            (c_lons >= visual_bbox[0]) & (c_lons <= visual_bbox[2]), c_lons,
            np.nan)
        c_lats = np.where(
            (c_lats >= visual_bbox[1]) & (c_lats <= visual_bbox[3]), c_lats,
            np.nan)
        #add bathymetry
        if stride == None:
            if visual_bbox[2] - visual_bbox[0] < 1.5:
                stride = 1
            else:
                stride = 2
        nc1 = netCDF4.Dataset(os.path.normpath(bathy))
        x = nc1.variables['x']
        y = nc1.variables['y']
        x_indexes = np.where((x[:] >= visual_bbox[0])
                             & (x[:] <= visual_bbox[2]))[0]
        y_indexes = np.where((y[:] >= visual_bbox[1])
                             & (y[:] <= visual_bbox[3]))[0]

        x_min = x_indexes[0]
        x_max = x_indexes[-1]
        y_min = y_indexes[0]
        y_max = y_indexes[-1]

        lons = x[x_min:x_max]
        lats = y[y_min:y_max]
        bath = nc1.variables['z'][y_min:y_max, x_min:x_max]
        bath[bath > 0] = 0
        #bath = bath.astype(np.float32)
        bath[bath < -800] = -800  #np.nan
        x_grid, y_grid = np.meshgrid(lons, lats)

        mpl_extent = matplotlib.transforms.Bbox.from_extents(
            visual_bbox[0], visual_bbox[1], visual_bbox[2], visual_bbox[3])

        CNorm = matplotlib.colors.Normalize(
            vmin=-400,
            vmax=300,
        )

        mgr = multiprocessing.Manager()
        fname = mgr.list()

        p_proj_lons = []
        p_proj_lats = []
        p_proj_depth = []

        lat = self.nc.variables['lat'][:, :]
        lon = self.nc.variables['lon'][:, :]
        depth = self.nc.variables['depth'][:, :]
        time = netCDF4.num2date(self.nc.variables['time'][:],
                                self.nc.variables['time'].units)

        datetimeformat = '%Y-%m-%d %H:%M'

        p = []
        c = 0
        length = self.nc.variables['particle'].shape[0]

        def render_frame(visual_bbox, c_lons, c_lat, mpl_extent, x_grid,
                         y_grid, bath, stride, view, length, lat, lon, depth,
                         temp_folder, frame_prefix, c, semo):
            import numpy as np
            import netCDF4, sys, os
            import matplotlib
            import matplotlib.pyplot
            from matplotlib import cm, animation
            from mpl_toolkits.mplot3d import Axes3D
            from matplotlib.ticker import MultipleLocator
            with semo:
                fig2 = matplotlib.pyplot.figure(figsize=(12, 6))
                ax2 = fig2.add_subplot(111, projection='3d')
                ax3 = fig2.add_axes([.75, .1, .15, .3])
                ax4 = fig2.add_axes([.2, .1, .15, .3])
                subbox = visual_bbox  #(self.nc.variables['lon'][:,0].min(), self.nc.variables['lat'][:,0].min(),
                #self.nc.variables['lon'][:,0].max(), self.nc.variables['lat'][:,0].max())
                ax3.plot(c_lons,
                         c_lats,
                         clip_box=mpl_extent,
                         clip_on=True,
                         color='c')  # shoreline

                ax3.set_xlim(subbox[0], subbox[2])
                ax3.set_ylim(subbox[1], subbox[3])
                #ax3.pcolor(x_grid, y_grid, bath, cmap="Blues_r", norm=CNorm)

                ax2.plot_surface(x_grid,
                                 y_grid,
                                 bath,
                                 rstride=stride,
                                 cstride=stride,
                                 cmap="Blues_r",
                                 linewidth=0.01,
                                 antialiased=False,
                                 norm=CNorm,
                                 shade=True,
                                 edgecolor='#6183A6')

                ax2.plot(c_lons, c_lats, np.zeros_like(c_lons))

                ax2.set_xlim3d(visual_bbox[0], visual_bbox[2])
                ax2.set_ylim3d(visual_bbox[1], visual_bbox[3])
                ax2.view_init(*view)

                ax2.set_title(time[i].strftime(datetimeformat) + " - " +
                              time[i + 2].strftime(datetimeformat))
                #ax2.set_zmargin(50)
                ax3.set_xlabel('Longitude')
                ax3.set_ylabel('Latitude')
                ax3.tick_params(axis='both', which='major', labelsize=10)
                ax3.yaxis.set_ticks_position('right')
                ax3.ticklabel_format(axis='x', style='plain')
                ax3.xaxis.set_major_locator(MultipleLocator(.5))
                ax3.grid(True)
                ax4.set_ylabel('Depth (m)')
                ax4.set_ylim(-200, 0)
                ax4.tick_params(axis='x', which='major', labelsize=10)
                #ax4.set_xlim(1,3)
                ax4.xaxis.set_ticklabels([])
                #ax3.xaxis.set_ticklabels(np.unique(c_lons.astype(int)))
                ax2.set_zlabel('Depth (m)')
                #ax2.set_frame_on(False)
                #ax2.set_position([0,0,1,1])
                ax2.xaxis.set_ticklabels([])
                ax2.yaxis.set_ticklabels([])
                #ax2.zaxis.set_ticklabels(['Surface'])
                ax2.zaxis.set_ticks(range(-800, 100, 200))
                ax2.grid(False)
                #ax2.set_zlim(-200, 100)

                for j in range(length):
                    # Each particle
                    ax2.plot(lon[i:i + 3, j],
                             lat[i:i + 3, j],
                             depth[i:i + 3, j],
                             ':',
                             c='r',
                             linewidth=2,
                             markersize=5,
                             markerfacecolor='r')
                    ax3.plot(
                        lon[:i + 3, j],
                        lat[:i + 3, j],
                        c='.2',
                        linewidth=.5,
                        markersize=5,
                        markerfacecolor='r',
                    )
                    ax3.scatter(lon[i + 2, j], lat[i + 2, j], c='r')
                    ax4.plot(range(i + 3),
                             depth[:i + 3, j],
                             c='r',
                             linewidth=.5,
                             aa=True)
                    ax4.scatter(np.ones_like(depth[i + 2, j]) * (i + 2),
                                depth[i + 2, j],
                                c='r')
                    if i == 2:
                        ax4.set_xlim(i - 2, i + 2.25)
                    elif i >= 3:
                        ax4.set_xlim(i - 3, i + 2.25)
                    else:
                        ax4.set_xlim(i, i + 2.25)
                #ax2.scatter(lon[i,:], lat[i,:], depth[i,:], zdir='z', c='r')
                ax2.set_zlim3d(-800, 25)

                image_path = os.path.join(temp_folder,
                                          '%s%04d.png' % (frame_prefix, c))
                fig2.savefig(image_path, dpi=350, bbox_inches='tight')
                fname.append(image_path)

                del ax2, ax3, ax4, subbox, fig2

        jobs = []
        for i in range(self.nc.variables['time'].shape[0])[:-4:2]:
            p = multiprocessing.Process(
                target=render_frame,
                args=(visual_bbox, c_lons, c_lats, mpl_extent, x_grid, y_grid,
                      bath, stride, view, length, lat, lon, depth, temp_folder,
                      frame_prefix, c, semo))
            p.start()
            jobs.append(p)
            c += 1

        for j in jobs:
            j.join(120)

        return save_animation(output, sorted(fname), frame_prefix=frame_prefix)