Пример #1
0
    def F(x, y):
        """This is the function returned by composite_quantity_setting_function
           It can be passed to set_quantity
        """
        isSet = numpy.zeros(len(x))  # 0/1 - record if each point has been set
        quantityVal = x * 0 + numpy.nan  # Function return value

        # Record points which evaluated to nan on their first preference
        # dataset.
        was_ever_nan = (x * 0).astype(int)

        lpf = len(poly_fun_pairs)
        if (lpf <= 0):
            raise Exception('Must have at least 1 fun-poly-pair')

        # Make an array of 'transformed' spatial coordinates, for checking
        # polygon inclusion
        xll = domain.geo_reference.xllcorner
        yll = domain.geo_reference.yllcorner
        xy_array_trans = numpy.vstack([x + xll, y + yll]).transpose()

        # Check that none of the pi polygons [except perhaps the last] is 'All'
        for i in range(lpf - 1):
            if (poly_fun_pairs[i][0] == 'All'):
                # This is only ok if all the othe poly_fun_pairs are None
                remaining_poly_fun_pairs_are_None = \
                    [poly_fun_pairs[j][0] is None for j in range(i+1,lpf)]
                if (not all(remaining_poly_fun_pairs_are_None)):
                    raise Exception('Can only have the last polygon = All')

        # Main Loop
        # Apply the fi inside the pi
        for i in range(lpf):
            fi = poly_fun_pairs[i][1]  # The function
            pi = poly_fun_pairs[i][0]  # The polygon

            # Quick exit
            if (pi is None):
                continue

            ###################################################################
            # Get indices fInds of points in polygon pi which are not already
            # set
            ###################################################################
            if (pi == 'All'):
                # Get all unset points
                fInside = (1 - isSet)
                fInds = (fInside == 1).nonzero()[0]

            else:

                if (pi == 'Extent'):
                    # Here fi MUST be a gdal-compatible raster
                    if (not (type(fi) == str)):
                        msg = ' pi = "Extent" can only be used when fi is a' +\
                              ' raster file name'
                        raise Exception(msg)

                    if (not os.path.exists(fi)):
                        msg = 'fi ' + str(fi) + ' is supposed to be a ' +\
                              ' raster filename, but it could not be found'
                        raise Exception(msg)

                    # Then we get the extent from the raster itself
                    pi_path = su.getRasterExtent(fi, asPolygon=True)

                    if verbose:
                        print 'Extracting extent from raster: ', fi
                        print 'Extent: ', pi_path

                elif ((type(pi) == str) and os.path.isfile(pi)):
                    # pi is a file
                    pi_path = su.read_polygon(pi)

                else:
                    # pi is the actual polygon data
                    pi_path = pi

                # Get the insides of unset points inside pi_path
                notSet = (isSet == 0.).nonzero()[0]
                fInds = inside_polygon(xy_array_trans[notSet, :], pi_path)
                fInds = notSet[fInds]

            if len(fInds) == 0:
                # No points found, move on
                continue

            ###################################################################
            # Evaluate fi at the points inside pi
            ###################################################################

            # We use various tricks to infer whether fi is a function,
            # a constant, a file (raster or csv), or an array
            if (hasattr(fi, '__call__')):
                # fi is a function
                quantityVal[fInds] = fi(x[fInds], y[fInds])

            elif isinstance(fi, (int, long, float)):
                # fi is a numerical constant
                quantityVal[fInds] = fi * 1.0

            elif (type(fi) is str and os.path.exists(fi)):
                # fi is a file which is assumed to be
                # a gdal-compatible raster OR an x,y,z elevation file
                if os.path.splitext(fi)[1] in ['.txt', '.csv']:
                    fi_array = su.read_csv_optional_header(fi)
                    # Check the results
                    if fi_array.shape[1] is not 3:
                        print 'Treated input file ' + fi +\
                              ' as xyz array with an optional header'
                        msg = 'Array should have 3 columns -- x,y,value'
                        raise Exception(msg)

                    newfi = make_nearestNeighbour_quantity_function(
                        fi_array,
                        domain,
                        k_nearest_neighbours=default_k_nearest_neighbours)
                    quantityVal[fInds] = newfi(x[fInds], y[fInds])

                else:
                    # Treating input file as a raster
                    newfi = quantityRasterFun(
                        domain, fi, interpolation=default_raster_interpolation)
                    quantityVal[fInds] = newfi(x[fInds], y[fInds])

            elif (type(fi) is numpy.ndarray):
                if fi.shape[1] is not 3:
                    msg = 'Array should have 3 columns -- x,y,value'
                    raise Exception(msg)
                newfi = make_nearestNeighbour_quantity_function(
                    fi,
                    domain,
                    k_nearest_neighbours=default_k_nearest_neighbours)
                quantityVal[fInds] = newfi(x[fInds], y[fInds])

            else:
                print 'Error with function from'
                print fi
                msg = 'Cannot make function from type ' + str(type(fi))
                raise Exception, msg

            ###################################################################
            # Check for nan values
            ###################################################################
            #nan_flag = (quantityVal[fInds] != quantityVal[fInds])
            nan_flag = 1 * numpy.isnan(quantityVal[fInds])
            nan_inds = nan_flag.nonzero()[0]
            was_ever_nan[fInds[nan_inds]] = 1

            if len(nan_inds) > 0:
                if nan_treatment == 'exception':
                    msg = 'nan values generated by the poly_fun_pair at '\
                          'index ' + str(i) + ' '\
                          'in composite_quantity_setting_function. ' + \
                          'To allow these values to be set by later ' + \
                          'poly_fun pairs, pass the argument ' + \
                          'nan_treatment="fall_through" ' + \
                          'to composite_quantity_setting_function'
                    raise Exception(msg)

                elif nan_treatment == 'fall_through':
                    msg = 'WARNING: nan values generated by the ' + \
                          'poly_fun_pair at index ' + str(i) + ' '\
                          'in composite_quantity_setting_function. ' + \
                          'They will be passed to later poly_fun_pairs'
                    if verbose: print msg
                    not_nan_inds = (1 - nan_flag).nonzero()[0]

                    if len(not_nan_inds) > 0:
                        fInds = fInds[not_nan_inds]
                    else:
                        # All values are nan
                        msg = '( Actually all the values were nan - ' + \
                              'Are you sure they should be? Possible error?)'
                        if verbose: print msg
                        continue

                else:
                    msg = 'Found nan values in ' + \
                          'composite_quantity_setting_function but ' + \
                          'nan_treatment is not a recognized value'
                    raise Exception(msg)

            # Record that the points have been set
            isSet[fInds] = 1

            # Enforce clip_range
            if clip_range is not None:
                lower_bound = clip_range[i][0]
                upper_bound = clip_range[i][1]
                quantityVal[fInds] = numpy.maximum(quantityVal[fInds],
                                                   lower_bound)
                quantityVal[fInds] = numpy.minimum(quantityVal[fInds],
                                                   upper_bound)

        # End of loop

        # Find points which were nan on their first preference dataset + are
        # inside nan_interpolation_region_polygon. Then reinterpolate their
        # values from the other x,y, quantityVal points.
        if (nan_interpolation_region_polygon is not None) &\
           (was_ever_nan.sum() > 0):
            if nan_interpolation_region_polygon == 'All':
                points_to_reinterpolate = was_ever_nan.nonzero()[0]
            else:
                # nan_interpolation_region_polygon contains information on 1 or
                # more polygons
                # Inside those polygons, we need to re-interpolate points which
                # first evaluted to na
                possible_points_to_reint = was_ever_nan.nonzero()[0]
                points_to_reinterpolate = numpy.array([]).astype(int)

                for i in range(len(nan_interpolation_region_polygon)):
                    nan_pi = nan_interpolation_region_polygon[i]

                    # Ensure nan_pi = list of x,y points making a polygon
                    if (type(nan_pi) == str):
                        nan_pi = su.read_polygon(nan_pi)

                    points_in_nan_pi = inside_polygon(
                        xy_array_trans[possible_points_to_reint, :], nan_pi)

                    if len(points_in_nan_pi) > 0:
                        points_to_reinterpolate = numpy.hstack([
                            points_to_reinterpolate,
                            possible_points_to_reint[points_in_nan_pi]
                        ])

            if verbose:
                print 'Re-interpolating ', len(points_to_reinterpolate),\
                      ' points which were nan under their',\
                      ' first-preference and are inside the',\
                      ' nan_interpolation_region_polygon'

            if len(points_to_reinterpolate) > 0:
                msg = 'WARNING: nan interpolation is being applied. This ',\
                      'should be done in serial prior to distributing the ',\
                      'domain, as there is no parallel communication ',\
                      'implemented yet [so parallel results might depend on ',\
                      'the number of processes]'
                if verbose:
                    print msg

            # Find the interpolation points = points not needing reinterpolation
            ip = x * 0 + 1
            ip[points_to_reinterpolate] = 0
            number_of_ip = ip.sum()
            ip = ip.nonzero()[0]

            # Check that none of the ip points has an nan value
            nan_ip = (quantityVal[ip] != quantityVal[ip]).nonzero()[0]

            if len(nan_ip) > 0:
                print 'There are ', len(nan_ip), ' points outside the ',\
                      'nan_interpolation_region_polygon have nan values.'
                print 'The user should ensure this does not happen.'
                print 'The points have the following coordinates:'
                print xy_array_trans[ip[nan_ip], :]
                msg = "There are nan points outside of " +\
                      "nan_interpolation_region_polygon, even after all " +\
                      "fall-through's"
                raise Exception(msg)

            if (number_of_ip < default_k_nearest_neighbours):
                raise Exception('Too few non-nan points to interpolate from')

            # Make function for re-interpolation. Note this requires
            # x,y,z in georeferenced coordinates, whereas x,y are ANUGA
            # coordinates
            reinterp_F = make_nearestNeighbour_quantity_function(
                numpy.vstack([
                    xy_array_trans[ip, 0], xy_array_trans[ip, 1],
                    quantityVal[ip]
                ]).transpose(),
                domain,
                k_nearest_neighbours=default_k_nearest_neighbours)

            # re-interpolate
            quantityVal[points_to_reinterpolate] = reinterp_F(
                x[points_to_reinterpolate], y[points_to_reinterpolate])

            isSet[points_to_reinterpolate] = 1

        # Check there are no remaining nan values
        if (min(isSet) != 1):
            print 'Some points remain as nan, which is not allowed'
            unset_inds = (isSet != 1).nonzero()[0]
            lui = min(5, len(unset_inds))
            print 'There are ', len(unset_inds), ' such points'
            print 'Here are a few:'
            for i in range(lui):
                print x[unset_inds[i]] + xll, y[unset_inds[i]] + yll
            raise Exception('It seems the input data needs to be fixed')

        return quantityVal
    def F(x,y):
        """This is the function returned by composite_quantity_setting_function
           It can be passed to set_quantity
        """
        isSet = numpy.zeros(len(x)) # 0/1 - record if each point has been set
        quantityVal = x*0 + numpy.nan # Function return value

        # Record points which evaluated to nan on their first preference
        # dataset.
        was_ever_nan = (x*0).astype(int)

        lpf = len(poly_fun_pairs)
        if(lpf <= 0):
            raise Exception('Must have at least 1 fun-poly-pair')

        # Make an array of 'transformed' spatial coordinates, for checking
        # polygon inclusion
        xll = domain.geo_reference.xllcorner
        yll = domain.geo_reference.yllcorner
        xy_array_trans = numpy.vstack([x+xll,y+yll]).transpose()

        # Check that none of the pi polygons [except perhaps the last] is 'All'
        for i in range(lpf-1):
            if(poly_fun_pairs[i][0]=='All'):
                # This is only ok if all the othe poly_fun_pairs are None
                remaining_poly_fun_pairs_are_None = \
                    [poly_fun_pairs[j][0] is None for j in range(i+1,lpf)]
                if(not all(remaining_poly_fun_pairs_are_None)):
                    raise Exception('Can only have the last polygon = All')

        # Main Loop
        # Apply the fi inside the pi
        for i in range(lpf):
            fi = poly_fun_pairs[i][1] # The function
            pi = poly_fun_pairs[i][0] # The polygon

            # Quick exit
            if(pi is None):
                continue

            ###################################################################
            # Get indices fInds of points in polygon pi which are not already
            # set
            ###################################################################
            if(pi == 'All'):
                # Get all unset points
                fInside = (1-isSet)
                fInds = (fInside==1).nonzero()[0]

            else:

                if(pi == 'Extent'):
                    # Here fi MUST be a gdal-compatible raster
                    if(not (type(fi) == str)):
                        msg = ' pi = "Extent" can only be used when fi is a' +\
                              ' raster file name'
                        raise Exception(msg)

                    if(not os.path.exists(fi)):
                        msg = 'fi ' + str(fi) + ' is supposed to be a ' +\
                              ' raster filename, but it could not be found'
                        raise Exception(msg)

                    # Then we get the extent from the raster itself
                    pi_path = su.getRasterExtent(fi,asPolygon=True)

                    if verbose:
                        print 'Extracting extent from raster: ', fi
                        print 'Extent: ', pi_path

                elif( (type(pi) == str) and os.path.isfile(pi) ): 
                    # pi is a file
                    pi_path = su.read_polygon(pi)

                else:
                    # pi is the actual polygon data
                    pi_path = pi

                # Get the insides of unset points inside pi_path
                notSet = (isSet==0.).nonzero()[0]
                fInds = inside_polygon(xy_array_trans[notSet,:], pi_path)
                fInds = notSet[fInds]

            if len(fInds) == 0:
                # No points found, move on
                continue

            ###################################################################
            # Evaluate fi at the points inside pi
            ###################################################################

            # We use various tricks to infer whether fi is a function,
            # a constant, a file (raster or csv), or an array
            if(hasattr(fi,'__call__')):
                # fi is a function
                quantityVal[fInds] = fi(x[fInds], y[fInds])

            elif isinstance(fi, (int, long, float)):
                # fi is a numerical constant
                quantityVal[fInds] = fi*1.0

            elif ( type(fi) is str and os.path.exists(fi)):
                # fi is a file which is assumed to be 
                # a gdal-compatible raster OR an x,y,z elevation file
                if os.path.splitext(fi)[1] in ['.txt', '.csv']:
                    fi_array = su.read_csv_optional_header(fi)
                    # Check the results
                    if fi_array.shape[1] is not 3:
                        print 'Treated input file ' + fi +\
                              ' as xyz array with an optional header'
                        msg = 'Array should have 3 columns -- x,y,value'
                        raise Exception(msg)

                    newfi = make_nearestNeighbour_quantity_function(
                        fi_array, domain, 
                        k_nearest_neighbours = default_k_nearest_neighbours)
                    quantityVal[fInds] = newfi(x[fInds], y[fInds])

                else:
                    # Treating input file as a raster
                    newfi = quantityRasterFun(domain, fi, 
                        interpolation = default_raster_interpolation)
                    quantityVal[fInds] = newfi(x[fInds], y[fInds])

            elif(type(fi) is numpy.ndarray):
                if fi.shape[1] is not 3:
                    msg = 'Array should have 3 columns -- x,y,value'
                    raise Exception(msg)
                newfi = make_nearestNeighbour_quantity_function(fi, domain, 
                    k_nearest_neighbours = default_k_nearest_neighbours)
                quantityVal[fInds] = newfi(x[fInds], y[fInds])

            else:
                print 'Error with function from'
                print fi
                msg='Cannot make function from type ' + str(type(fi))
                raise Exception, msg 

            ###################################################################
            # Check for nan values
            ###################################################################
            #nan_flag = (quantityVal[fInds] != quantityVal[fInds])
            nan_flag = 1*numpy.isnan(quantityVal[fInds])
            nan_inds = nan_flag.nonzero()[0]
            was_ever_nan[fInds[nan_inds]] = 1

            if len(nan_inds)>0:
                if nan_treatment == 'exception':
                    msg = 'nan values generated by the poly_fun_pair at '\
                          'index ' + str(i) + ' '\
                          'in composite_quantity_setting_function. ' + \
                          'To allow these values to be set by later ' + \
                          'poly_fun pairs, pass the argument ' + \
                          'nan_treatment="fall_through" ' + \
                          'to composite_quantity_setting_function' 
                    raise Exception(msg)

                elif nan_treatment == 'fall_through':
                    msg = 'WARNING: nan values generated by the ' + \
                          'poly_fun_pair at index ' + str(i) + ' '\
                          'in composite_quantity_setting_function. ' + \
                          'They will be passed to later poly_fun_pairs'
                    if verbose: print msg
                    not_nan_inds = (1-nan_flag).nonzero()[0]

                    if len(not_nan_inds)>0:
                        fInds = fInds[not_nan_inds]
                    else:
                        # All values are nan
                        msg = '( Actually all the values were nan - ' + \
                              'Are you sure they should be? Possible error?)'
                        if verbose: print msg
                        continue

                else:
                    msg = 'Found nan values in ' + \
                          'composite_quantity_setting_function but ' + \
                          'nan_treatment is not a recognized value'
                    raise Exception(msg)

            # Record that the points have been set
            isSet[fInds] = 1

            # Enforce clip_range
            if clip_range is not None:
                lower_bound = clip_range[i][0]
                upper_bound = clip_range[i][1]
                quantityVal[fInds] = numpy.maximum(
                    quantityVal[fInds], lower_bound)
                quantityVal[fInds] = numpy.minimum(
                    quantityVal[fInds], upper_bound)

        # End of loop

        # Find points which were nan on their first preference dataset + are
        # inside nan_interpolation_region_polygon. Then reinterpolate their
        # values from the other x,y, quantityVal points.
        if (nan_interpolation_region_polygon is not None) &\
           (was_ever_nan.sum() > 0):
            if nan_interpolation_region_polygon == 'All':
                points_to_reinterpolate = was_ever_nan.nonzero()[0]
            else:
                # nan_interpolation_region_polygon contains information on 1 or
                # more polygons
                # Inside those polygons, we need to re-interpolate points which
                # first evaluted to na
                possible_points_to_reint = was_ever_nan.nonzero()[0]
                points_to_reinterpolate = numpy.array([]).astype(int) 

                for i in range(len(nan_interpolation_region_polygon)):
                    nan_pi = nan_interpolation_region_polygon[i]

                    # Ensure nan_pi = list of x,y points making a polygon
                    if(type(nan_pi) == str):
                        nan_pi = su.read_polygon(nan_pi)
              
                    points_in_nan_pi = inside_polygon(
                        xy_array_trans[possible_points_to_reint,:],
                        nan_pi) 
                    
                    if len(points_in_nan_pi)>0: 
                        points_to_reinterpolate = numpy.hstack(
                            [points_to_reinterpolate,
                             possible_points_to_reint[points_in_nan_pi]])


            if verbose: 
                print 'Re-interpolating ', len(points_to_reinterpolate),\
                      ' points which were nan under their',\
                      ' first-preference and are inside the',\
                      ' nan_interpolation_region_polygon'

            if len(points_to_reinterpolate) > 0:
                msg = 'WARNING: nan interpolation is being applied. This ',\
                      'should be done in serial prior to distributing the ',\
                      'domain, as there is no parallel communication ',\
                      'implemented yet [so parallel results might depend on ',\
                      'the number of processes]'
                if verbose:
                    print msg
                    

            # Find the interpolation points = points not needing reinterpolation
            ip = x*0 + 1
            ip[points_to_reinterpolate] = 0
            number_of_ip = ip.sum()
            ip = ip.nonzero()[0]

            # Check that none of the ip points has an nan value
            nan_ip = (quantityVal[ip] != quantityVal[ip]).nonzero()[0]

            if len(nan_ip) > 0:
                print 'There are ', len(nan_ip), ' points outside the ',\
                      'nan_interpolation_region_polygon have nan values.'
                print 'The user should ensure this does not happen.'
                print 'The points have the following coordinates:'
                print xy_array_trans[ip[nan_ip],:]
                msg = "There are nan points outside of " +\
                      "nan_interpolation_region_polygon, even after all " +\
                      "fall-through's"
                raise Exception(msg)
         
            if(number_of_ip < default_k_nearest_neighbours):
                raise Exception('Too few non-nan points to interpolate from') 

            # Make function for re-interpolation. Note this requires
            # x,y,z in georeferenced coordinates, whereas x,y are ANUGA
            # coordinates
            reinterp_F = make_nearestNeighbour_quantity_function(
                numpy.vstack([xy_array_trans[ip,0], xy_array_trans[ip,1],
                              quantityVal[ip]]).transpose(),
                domain, 
                k_nearest_neighbours = default_k_nearest_neighbours)

            # re-interpolate
            quantityVal[points_to_reinterpolate] = reinterp_F(
                x[points_to_reinterpolate], y[points_to_reinterpolate])

            isSet[points_to_reinterpolate] = 1
            
        # Check there are no remaining nan values
        if( min(isSet) != 1):
            print 'Some points remain as nan, which is not allowed'
            unset_inds = (isSet != 1).nonzero()[0]
            lui = min(5, len(unset_inds)) 
            print 'There are ', len(unset_inds), ' such points'
            print 'Here are a few:'
            for i in range(lui):
                print x[unset_inds[i]] + xll, y[unset_inds[i]] + yll
            raise Exception('It seems the input data needs to be fixed')

        return quantityVal