示例#1
0
class ComponentMover(CurrentMoversBase):
    _schema = ComponentMoverSchema

    def __init__(self,
                 filename1=None,
                 filename2=None,
                 scale_refpoint=None,
                 pat1_angle=0,
                 pat1_speed=10,
                 pat1_speed_units=2,
                 pat1_scale_to_value=0.1,
                 pat2_angle=90,
                 pat2_scale_to_value=0.1,
                 pat2_speed=10,
                 pat2_speed_units=2,
                 scale_by=0,
                 wind=None,
                 **kwargs):
        """
        Uses super to invoke base class __init__ method.

        :param filename: file containing currents for first Cats pattern

        Optional parameters (kwargs).
        Defaults are defined by CyCatsMover object.

        :param filename: file containing currents for second Cats pattern

        :param wind: A gnome.environment.Wind object to be used to drive the
                     CatsMovers.  Will want a warning that mover will
                     not be active without a wind
        :param scale: A boolean to indicate whether to scale value
                      at reference point or not
        :param scale_value: Value used for scaling at reference point
        :param scale_refpoint: Reference location (long, lat, z).
                               The scaling applied to all data is determined
                               by scaling the raw value at this location.

        Remaining kwargs are passed onto Mover's __init__ using super.
        See Mover documentation for remaining valid kwargs.
        """

        if filename1 and not os.path.exists(filename1):
            raise ValueError(
                'Path for Cats filename1 does not exist: {0}'.format(
                    filename1))

        if filename2 is not None:
            if not os.path.exists(filename2):
                raise ValueError(
                    'Path for Cats filename2 does not exist: {0}'.format(
                        filename2))

        self.filename1 = filename1
        self.filename2 = filename2

        self.mover = CyComponentMover()
        self.mover.text_read(filename1, filename2)
        super(ComponentMover, self).__init__(**kwargs)

        self._wind = None
        if wind is not None:
            self.wind = wind
        self.scale_by = scale_by
        self.scale_refpoint = scale_refpoint
        self.pat1_angle = pat1_angle
        self.pat1_speed = pat1_speed
        self.pat1_speed_units = pat1_speed_units
        self.pat1_scale_to_value = pat1_scale_to_value
        self.pat2_angle = pat2_angle
        self.pat2_scale_to_value = pat2_scale_to_value
        self.pat2_speed = pat2_speed
        self.pat2_speed_units = pat2_speed_units

    def __repr__(self):
        """
        unambiguous representation of object
        """
        return 'ComponentMover(filename={0})'.format(self.filename1)

    # Properties
    pat1_angle = property(
        lambda self: self.mover.pat1_angle,
        lambda self, val: setattr(self.mover, 'pat1_angle', val))

    pat1_speed = property(
        lambda self: self.mover.pat1_speed,
        lambda self, val: setattr(self.mover, 'pat1_speed', val))

    pat1_speed_units = property(
        lambda self: self.mover.pat1_speed_units,
        lambda self, val: setattr(self.mover, 'pat1_speed_units', val))

    pat1_scale_to_value = property(
        lambda self: self.mover.pat1_scale_to_value,
        lambda self, val: setattr(self.mover, 'pat1_scale_to_value', val))

    pat2_angle = property(
        lambda self: self.mover.pat2_angle,
        lambda self, val: setattr(self.mover, 'pat2_angle', val))

    pat2_speed = property(
        lambda self: self.mover.pat2_speed,
        lambda self, val: setattr(self.mover, 'pat2_speed', val))

    pat2_speed_units = property(
        lambda self: self.mover.pat2_speed_units,
        lambda self, val: setattr(self.mover, 'pat2_speed_units', val))

    pat2_scale_to_value = property(
        lambda self: self.mover.pat2_scale_to_value,
        lambda self, val: setattr(self.mover, 'pat2_scale_to_value', val))

    scale_by = property(lambda self: self.mover.scale_by,
                        lambda self, val: setattr(self.mover, 'scale_by', val))

    extrapolate = property(
        lambda self: self.mover.extrapolate,
        lambda self, val: setattr(self.mover, 'extrapolate', val))

    use_averaged_winds = property(
        lambda self: self.mover.use_averaged_winds,
        lambda self, val: setattr(self.mover, 'use_averaged_winds', val))

    wind_power_factor = property(
        lambda self: self.mover.wind_power_factor,
        lambda self, val: setattr(self.mover, 'wind_power_factor', val))

    past_hours_to_average = property(
        lambda self: (self.mover.past_hours_to_average),
        lambda self, val: setattr(self.mover, 'past_hours_to_average', val))

    scale_factor_averaged_winds = property(
        lambda self: self.mover.scale_factor_averaged_winds, lambda self, val:
        setattr(self.mover, 'scale_factor_averaged_winds', val))

    use_original_scale_factor = property(
        lambda self: self.mover.use_original_scale_factor, lambda self, val:
        setattr(self.mover, 'use_original_scale_factor', val))

    @property
    def data_start(self):
        if self.wind is not None:
            return self.wind.data_start
        else:
            return MinusInfTime()

    @property
    def data_stop(self):
        if self.wind is not None:
            return self.wind.data_stop
        else:
            return InfTime()

    @property
    def scale_refpoint(self):
        return self.mover.ref_point

    @scale_refpoint.setter
    def scale_refpoint(self, val):
        '''
        Must be a tuple of length 2 or 3: (long, lat, z). If only (long, lat)
        is given, the set z = 0
        '''
        if val is None:
            return
        if len(val) == 2:
            self.mover.ref_point = (val[0], val[1], 0.)
        else:
            self.mover.ref_point = val

    @property
    def wind(self):
        return self._wind

    @wind.setter
    def wind(self, wind_obj):
        if not isinstance(wind_obj, Wind):
            self._wind = None
            return

        self.mover.set_ossm(wind_obj.ossm)
        self._wind = wind_obj

    def get_grid_data(self):
        """
            Invokes the GetToplogyHdl method of TriGridVel_c object
        """
        return self.get_triangles()

    def get_center_points(self):
        return self.get_triangle_center_points()

    def get_optimize_values(self, model_time):
        optimize_pat1 = self.mover.get_optimize_value(model_time, 1)
        optimize_pat2 = self.mover.get_optimize_value(model_time, 2)
        return optimize_pat1, optimize_pat2

    def get_scaled_velocities(self, model_time):
        """
        Get file values scaled to optimized
        check if pat2 exists
        """
        pat = 1
        vels_pat1 = self.mover._get_velocity_handle(pat)
        pat = 2
        vels_pat2 = self.mover._get_velocity_handle(pat)

        optimize_pat1, optimize_pat2 = self.get_optimize_values(model_time)

        vels_pat1['u'] = vels_pat1['u'] * optimize_pat1
        vels_pat1['v'] = vels_pat1['v'] * optimize_pat1

        if vels_pat2 != 0 and optimize_pat2 != 0:
            vels_pat1['u'] = vels_pat1['u'] + vels_pat2['u'] * optimize_pat2
            vels_pat1['v'] = vels_pat1['v'] + vels_pat2['v'] * optimize_pat2

        return vels_pat1
示例#2
0
class ComponentMover(CurrentMoversBase):
    _schema = ComponentMoverSchema

    def __init__(self,
                 filename1=None,
                 filename2=None,
                 scale_refpoint=None,
                 pat1_angle=0,
                 pat1_speed=10,
                 pat1_speed_units=2,
                 pat1_scale_to_value=0.1,
                 pat2_angle=90,
                 pat2_scale_to_value=0.1,
                 pat2_speed=10,
                 pat2_speed_units=2,
                 scale_by=0,
                 wind=None,
                 **kwargs):
        """
        Uses super to invoke base class __init__ method.

        :param filename: file containing currents for first Cats pattern

        Optional parameters (kwargs).
        Defaults are defined by CyCatsMover object.

        :param filename: file containing currents for second Cats pattern

        :param wind: A gnome.environment.Wind object to be used to drive the
                     CatsMovers.  Will want a warning that mover will
                     not be active without a wind
        :param scale: A boolean to indicate whether to scale value
                      at reference point or not
        :param scale_value: Value used for scaling at reference point
        :param scale_refpoint: Reference location (long, lat, z).
                               The scaling applied to all data is determined
                               by scaling the raw value at this location.

        Remaining kwargs are passed onto Mover's __init__ using super.
        See Mover documentation for remaining valid kwargs.
        """

        if filename1 and not os.path.exists(filename1):
            raise ValueError('Path for Cats filename1 does not exist: {0}'
                             .format(filename1))

        if filename2 is not None:
            if not os.path.exists(filename2):
                raise ValueError('Path for Cats filename2 does not exist: {0}'
                                 .format(filename2))

        self.filename1 = filename1
        self.filename2 = filename2

        self.mover = CyComponentMover()
        self.mover.text_read(filename1, filename2)
        super(ComponentMover, self).__init__(**kwargs)


        self._wind = None
        if wind is not None:
            self.wind = wind
        self.scale_by = scale_by
        self.scale_refpoint = scale_refpoint
        self.pat1_angle = pat1_angle
        self.pat1_speed = pat1_speed
        self.pat1_speed_units = pat1_speed_units
        self.pat1_scale_to_value = pat1_scale_to_value
        self.pat2_angle = pat2_angle
        self.pat2_scale_to_value = pat2_scale_to_value
        self.pat2_speed = pat2_speed
        self.pat2_speed_units = pat2_speed_units

    def __repr__(self):
        """
        unambiguous representation of object
        """
        return 'ComponentMover(filename={0})'.format(self.filename1)

    # Properties
    pat1_angle = property(lambda self: self.mover.pat1_angle,
                          lambda self, val: setattr(self.mover, 'pat1_angle',
                                                    val))

    pat1_speed = property(lambda self: self.mover.pat1_speed,
                          lambda self, val: setattr(self.mover, 'pat1_speed',
                                                    val))

    pat1_speed_units = property(lambda self: self.mover.pat1_speed_units,
                                lambda self, val: setattr(self.mover,
                                                          'pat1_speed_units',
                                                          val))

    pat1_scale_to_value = property(lambda self: self.mover.pat1_scale_to_value,
                                   lambda self, val:
                                   setattr(self.mover, 'pat1_scale_to_value',
                                           val))

    pat2_angle = property(lambda self: self.mover.pat2_angle,
                          lambda self, val: setattr(self.mover, 'pat2_angle',
                                                    val))

    pat2_speed = property(lambda self: self.mover.pat2_speed,
                          lambda self, val: setattr(self.mover, 'pat2_speed',
                                                    val))

    pat2_speed_units = property(lambda self: self.mover.pat2_speed_units,
                                lambda self, val: setattr(self.mover,
                                                          'pat2_speed_units',
                                                          val))

    pat2_scale_to_value = property(lambda self: self.mover.pat2_scale_to_value,
                                   lambda self, val:
                                   setattr(self.mover, 'pat2_scale_to_value',
                                           val))

    scale_by = property(lambda self: self.mover.scale_by,
                        lambda self, val: setattr(self.mover, 'scale_by', val))

    extrapolate = property(lambda self: self.mover.extrapolate,
                           lambda self, val: setattr(self.mover,
                                                     'extrapolate',
                                                     val))

    use_averaged_winds = property(lambda self: self.mover.use_averaged_winds,
                                  lambda self, val:
                                  setattr(self.mover, 'use_averaged_winds',
                                          val))

    wind_power_factor = property(lambda self: self.mover.wind_power_factor,
                                 lambda self, val: setattr(self.mover,
                                                           'wind_power_factor',
                                                           val))

    past_hours_to_average = property(lambda self: (self.mover
                                                   .past_hours_to_average),
                                     lambda self, val:
                                     setattr(self.mover,
                                             'past_hours_to_average', val))

    scale_factor_averaged_winds = property(lambda self: self.mover.scale_factor_averaged_winds,
                                           lambda self, val: setattr(self.mover,
                                                                     'scale_factor_averaged_winds',
                                                                     val))

    use_original_scale_factor = property(lambda self: self.mover.use_original_scale_factor,
                                         lambda self, val: setattr(self.mover,
                                                                   'use_original_scale_factor',
                                                                   val))

    @property
    def data_start(self):
        if self.wind is not None:
            return self.wind.data_start
        else:
            return MinusInfTime()

    @property
    def data_stop(self):
        if self.wind is not None:
            return self.wind.data_stop
        else:
            return InfTime()

    @property
    def scale_refpoint(self):
        return self.mover.ref_point

    @scale_refpoint.setter
    def scale_refpoint(self, val):
        '''
        Must be a tuple of length 2 or 3: (long, lat, z). If only (long, lat)
        is given, the set z = 0
        '''
        if val is None:
            return
        if len(val) == 2:
            self.mover.ref_point = (val[0], val[1], 0.)
        else:
            self.mover.ref_point = val

    @property
    def wind(self):
        return self._wind

    @wind.setter
    def wind(self, wind_obj):
        if not isinstance(wind_obj, Wind):
            self._wind = None
            return

        self.mover.set_ossm(wind_obj.ossm)
        self._wind = wind_obj

    def get_grid_data(self):
        """
            Invokes the GetToplogyHdl method of TriGridVel_c object
        """
        return self.get_triangles()

    def get_center_points(self):
        return self.get_triangle_center_points()

    def get_optimize_values(self, model_time):
        optimize_pat1 = self.mover.get_optimize_value(model_time, 1)
        optimize_pat2 = self.mover.get_optimize_value(model_time, 2)
        return optimize_pat1, optimize_pat2

    def get_scaled_velocities(self, model_time):
        """
        Get file values scaled to optimized
        check if pat2 exists
        """
        pat = 1
        vels_pat1 = self.mover._get_velocity_handle(pat)
        pat = 2
        vels_pat2 = self.mover._get_velocity_handle(pat)

        optimize_pat1, optimize_pat2 = self.get_optimize_values(model_time)

        vels_pat1['u'] = vels_pat1['u'] * optimize_pat1
        vels_pat1['v'] = vels_pat1['v'] * optimize_pat1

        if vels_pat2 != 0 and optimize_pat2 != 0:
            vels_pat1['u'] = vels_pat1['u'] + vels_pat2['u'] * optimize_pat2
            vels_pat1['v'] = vels_pat1['v'] + vels_pat2['v'] * optimize_pat2

        return vels_pat1
示例#3
0
class ComponentMover(CurrentMoversBase, Serializable):

    # _state = copy.deepcopy(CyMover._state)
    _state = copy.deepcopy(CurrentMoversBase._state)

    _update = ['scale_refpoint',
               'pat1_angle', 'pat1_speed', 'pat1_speed_units',
               'pat1_scale_to_value',
               'pat2_angle', 'pat2_speed', 'pat2_speed_units',
               'pat2_scale_to_value', 'scale_by']
    _create = []
    _create.extend(_update)
    _state.add(update=_update, save=_create)
    _state.add_field([Field('filename1', save=True, read=True, isdatafile=True,
                            test_for_eq=False),
                      Field('filename2', save=True, read=True, isdatafile=True,
                            test_for_eq=False),
                      Field('wind', save=True, update=True,
                            save_reference=True)])

    _schema = ComponentMoverSchema

    def __init__(self, filename1, filename2=None, wind=None,
                 **kwargs):
        """
        Uses super to invoke base class __init__ method.

        :param filename: file containing currents for first Cats pattern

        Optional parameters (kwargs).
        Defaults are defined by CyCatsMover object.

        :param filename: file containing currents for second Cats pattern

        :param wind: A gnome.environment.Wind object to be used to drive the
                     CatsMovers.  Will want a warning that mover will
                     not be active without a wind
        :param scale: A boolean to indicate whether to scale value
                      at reference point or not
        :param scale_value: Value used for scaling at reference point
        :param scale_refpoint: Reference location (long, lat, z).
                               The scaling applied to all data is determined
                               by scaling the raw value at this location.

        Remaining kwargs are passed onto Mover's __init__ using super.
        See Mover documentation for remaining valid kwargs.
        """

        if not os.path.exists(filename1):
            raise ValueError('Path for Cats filename1 does not exist: {0}'
                             .format(filename1))

        if filename2 is not None:
            if not os.path.exists(filename2):
                raise ValueError('Path for Cats filename2 does not exist: {0}'
                                 .format(filename2))

        self.filename1 = filename1
        self.filename2 = filename2

        self.mover = CyComponentMover()
        self.mover.text_read(filename1, filename2)

        self._wind = None
        if wind is not None:
            self.wind = wind

        # TODO: no need to check for None since properties that are None
        #       are not persisted

        # I think this is required...
        if 'scale_refpoint' in kwargs:
            self.scale_refpoint = kwargs.pop('scale_refpoint')

        super(ComponentMover, self).__init__(**kwargs)

    def __repr__(self):
        """
        unambiguous representation of object
        """
        return 'ComponentMover(filename={0})'.format(self.filename1)

    # Properties
    pat1_angle = property(lambda self: self.mover.pat1_angle,
                          lambda self, val: setattr(self.mover, 'pat1_angle',
                                                    val))

    pat1_speed = property(lambda self: self.mover.pat1_speed,
                          lambda self, val: setattr(self.mover, 'pat1_speed',
                                                    val))

    pat1_speed_units = property(lambda self: self.mover.pat1_speed_units,
                                lambda self, val: setattr(self.mover,
                                                          'pat1_speed_units',
                                                          val))

    pat1_scale_to_value = property(lambda self: self.mover.pat1_scale_to_value,
                                   lambda self, val:
                                   setattr(self.mover, 'pat1_scale_to_value',
                                           val))

    pat2_angle = property(lambda self: self.mover.pat2_angle,
                          lambda self, val: setattr(self.mover, 'pat2_angle',
                                                    val))

    pat2_speed = property(lambda self: self.mover.pat2_speed,
                          lambda self, val: setattr(self.mover, 'pat2_speed',
                                                    val))

    pat2_speed_units = property(lambda self: self.mover.pat2_speed_units,
                                lambda self, val: setattr(self.mover,
                                                          'pat2_speed_units',
                                                          val))

    pat2_scale_to_value = property(lambda self: self.mover.pat2_scale_to_value,
                                   lambda self, val:
                                   setattr(self.mover, 'pat2_scale_to_value',
                                           val))

    scale_by = property(lambda self: self.mover.scale_by,
                        lambda self, val: setattr(self.mover, 'scale_by', val))

    extrapolate = property(lambda self: self.mover.extrapolate,
                           lambda self, val: setattr(self.mover,
                                                     'extrapolate',
                                                     val))

    use_averaged_winds = property(lambda self: self.mover.use_averaged_winds,
                                  lambda self, val:
                                  setattr(self.mover, 'use_averaged_winds',
                                          val))

    wind_power_factor = property(lambda self: self.mover.wind_power_factor,
                                 lambda self, val: setattr(self.mover,
                                                           'wind_power_factor',
                                                           val))

    past_hours_to_average = property(lambda self: (self.mover
                                                   .past_hours_to_average),
                                     lambda self, val:
                                     setattr(self.mover,
                                             'past_hours_to_average', val))

    scale_factor_averaged_winds = property(lambda self: self.mover.scale_factor_averaged_winds,
                                           lambda self, val: setattr(self.mover,
                                                                     'scale_factor_averaged_winds',
                                                                     val))

    use_original_scale_factor = property(lambda self: self.mover.use_original_scale_factor,
                                         lambda self, val: setattr(self.mover,
                                                                   'use_original_scale_factor',
                                                                   val))

    @property
    def scale_refpoint(self):
        return self.mover.ref_point

    @scale_refpoint.setter
    def scale_refpoint(self, val):
        '''
        Must be a tuple of length 2 or 3: (long, lat, z). If only (long, lat)
        is given, the set z = 0
        '''
        if len(val) == 2:
            self.mover.ref_point = (val[0], val[1], 0.)
        else:
            self.mover.ref_point = val

    @property
    def wind(self):
        return self._wind

    @wind.setter
    def wind(self, wind_obj):
        if not isinstance(wind_obj, Wind):
            raise TypeError('wind must be of type environment.Wind')

        self.mover.set_ossm(wind_obj.ossm)
        self._wind = wind_obj

    def get_grid_data(self):
        """
            Invokes the GetToplogyHdl method of TriGridVel_c object
        """
        return self.get_triangles()

    def get_center_points(self):
        return self.get_triangle_center_points()

    def get_scaled_velocities(self, model_time):
        """
        Get file values scaled to ref pt value, with tide applied (if any)
        """
        return self.mover._get_velocity_handle()

    def serialize(self, json_='webapi'):
        """
        Since 'wind' property is saved as a reference when used in save file
        and 'save' option, need to add appropriate node to WindMover schema
        """
        dict_ = self.to_serialize(json_)
        schema = self.__class__._schema()

        if json_ == 'webapi' and 'wind' in dict_:
            schema.add(WindSchema(name='wind'))

        return schema.serialize(dict_)

    @classmethod
    def deserialize(cls, json_):
        """
        append correct schema for wind object
        """
        schema = cls._schema()

        if 'wind' in json_:
            # for 'webapi', there will be nested Wind structure
            # for 'save' option, there should be no nested 'wind'. It is
            # removed, loaded and added back after deserialization
            schema.add(WindSchema())

        return schema.deserialize(json_)
class ComponentMover(CyMover, serializable.Serializable):

    _state = copy.deepcopy(CyMover._state)

    _update = ['scale_refpoint',
               'pat1_angle', 'pat1_speed', 'pat1_speed_units',
               'pat1_scale_to_value',
               'pat2_angle', 'pat2_speed', 'pat2_speed_units',
               'pat2_scale_to_value', 'scale_by']
    _create = []
    _create.extend(_update)
    _state.add(update=_update, save=_create)
    _state.add_field([serializable.Field('filename1',
                                         save=True, read=True, isdatafile=True,
                                         test_for_eq=False),
                      serializable.Field('filename2',
                                         save=True, read=True, isdatafile=True,
                                         test_for_eq=False),
                      serializable.Field('wind',
                                         save=True, update=True,
                                         save_reference=True)])
    _schema = ComponentMoverSchema

    def __init__(self, filename1, filename2=None, wind=None,
                 **kwargs):
        """
        Uses super to invoke base class __init__ method.

        :param filename: file containing currents for first Cats pattern

        Optional parameters (kwargs).
        Defaults are defined by CyCatsMover object.

        :param filename: file containing currents for second Cats pattern

        :param wind: A gnome.environment.Wind object to be used to drive the
                     CatsMovers.  Will want a warning that mover will
                     not be active without a wind
        :param scale: A boolean to indicate whether to scale value
                      at reference point or not
        :param scale_value: Value used for scaling at reference point
        :param scale_refpoint: Reference location (long, lat, z).
                               The scaling applied to all data is determined
                               by scaling the raw value at this location.

        Remaining kwargs are passed onto Mover's __init__ using super.
        See Mover documentation for remaining valid kwargs.
        """

        if not os.path.exists(filename1):
            raise ValueError('Path for Cats filename1 does not exist: {0}'
                             .format(filename1))

        if filename2 is not None:
            if not os.path.exists(filename2):
                raise ValueError('Path for Cats filename2 does not exist: {0}'
                                 .format(filename2))

        self.filename1 = filename1
        self.filename2 = filename2

        self.mover = CyComponentMover()
        self.mover.text_read(filename1, filename2)

        self._wind = None
        if wind is not None:
            self.wind = wind

        # self.scale = kwargs.pop('scale', self.mover.scale_type)
        # self.scale_value = kwargs.get('scale_value',
        #                               self.mover.scale_value)

        # TODO: no need to check for None since properties that are None
        #       are not persisted

        # I think this is required...
        if 'scale_refpoint' in kwargs:
            self.scale_refpoint = kwargs.pop('scale_refpoint')

#         if self.scale and self.scale_value != 0.0 \
#             and self.scale_refpoint is None:
#             raise TypeError("Provide a reference point in 'scale_refpoint'."
#                             )

        super(ComponentMover, self).__init__(**kwargs)

    def __repr__(self):
        """
        unambiguous representation of object
        """
        return 'ComponentMover(filename={0})'.format(self.filename1)

    # Properties

    # scale_type = property(lambda self: bool(self.mover.scale_type),
    #                       lambda self, val: setattr(self.mover, 'scale_type',
    #                                                 int(val)))

    # scale_by = property(lambda self: bool(self.mover.scale_by),
    #                     lambda self, val: setattr(self.mover, 'scale_by',
    #                                               int(val)))

    pat1_angle = property(lambda self: self.mover.pat1_angle,
                          lambda self, val: setattr(self.mover, 'pat1_angle',
                                                    val))

    pat1_speed = property(lambda self: self.mover.pat1_speed,
                          lambda self, val: setattr(self.mover, 'pat1_speed',
                                                    val))

    pat1_speed_units = property(lambda self: self.mover.pat1_speed_units,
                                lambda self, val: setattr(self.mover,
                                                          'pat1_speed_units',
                                                          val))

    pat1_scale_to_value = property(lambda self: self.mover.pat1_scale_to_value,
                                   lambda self, val:
                                   setattr(self.mover, 'pat1_scale_to_value',
                                           val))

    pat2_angle = property(lambda self: self.mover.pat2_angle,
                          lambda self, val: setattr(self.mover, 'pat2_angle',
                                                    val))

    pat2_speed = property(lambda self: self.mover.pat2_speed,
                          lambda self, val: setattr(self.mover, 'pat2_speed',
                                                    val))

    pat2_speed_units = property(lambda self: self.mover.pat2_speed_units,
                                lambda self, val: setattr(self.mover,
                                                          'pat2_speed_units',
                                                          val))

    pat2_scale_to_value = property(lambda self: self.mover.pat2_scale_to_value,
                                   lambda self, val:
                                   setattr(self.mover, 'pat2_scale_to_value',
                                           val))

    scale_by = property(lambda self: self.mover.scale_by,
                        lambda self, val: setattr(self.mover, 'scale_by', val))

    @property
    def scale_refpoint(self):
        return self.mover.ref_point

    @scale_refpoint.setter
    def scale_refpoint(self, val):
        '''
        Must be a tuple of length 2 or 3: (long, lat, z). If only (long, lat)
        is given, the set z = 0
        '''
        if len(val) == 2:
            self.mover.ref_point = (val[0], val[1], 0.)
        else:
            self.mover.ref_point = val

    @property
    def wind(self):
        return self._wind

    @wind.setter
    def wind(self, wind_obj):
        if not isinstance(wind_obj, environment.Wind):
            raise TypeError('wind must be of type environment.Wind')

        self.mover.set_ossm(wind_obj.ossm)
        self._wind = wind_obj

    def serialize(self, json_='webapi'):
        """
        Since 'wind' property is saved as a reference when used in save file
        and 'save' option, need to add appropriate node to WindMover schema
        """
        dict_ = self.to_serialize(json_)
        schema = self.__class__._schema()

        if json_ == 'webapi' and 'wind' in dict_:
            schema.add(environment.WindSchema(name='wind'))

        return schema.serialize(dict_)

    @classmethod
    def deserialize(cls, json_):
        """
        append correct schema for wind object
        """
        schema = cls._schema()

        if 'wind' in json_:
            # for 'webapi', there will be nested Wind structure
            # for 'save' option, there should be no nested 'wind'. It is
            # removed, loaded and added back after deserialization
            schema.add(environment.WindSchema())
        _to_dict = schema.deserialize(json_)

        return _to_dict