def get_phenology( date, result_location, image_ds, after=False, before=False, qa=False, ndv=-9999, pattern=_result_record ): """ Output a raster containing phenology information Phenology information includes spring_doy, autumn_doy, pheno_cor, peak_evi, peak_doy, and pheno_nobs. Args: date (int): Ordinal date for prediction image result_location (str): Location of the results image_ds (gdal.Dataset): Example dataset after (bool, optional): If date intersects a disturbed period, use next available time segment before (bool, optional): If date does not intersect a model, use previous non-disturbed time segment qa (bool, optional): Add QA flag specifying segment type (intersect, after, or before) ndv (int, optional): NoDataValue pattern (str, optional): filename pattern of saved record results Returns: tuple (np.ndarray, list): A tuple (np.ndarray, list) containing the 3D np.ndarray of the phenology metrics, and the band names for the output dataset """ # Find results records = find_results(result_location, pattern) n_bands = 7 attributes = ["spring_doy", "autumn_doy", "pheno_cor", "peak_evi", "peak_doy", "pheno_nobs"] band_names = ["SpringDOY", "AutumnDOY", "Pheno_R*10000", "Peak_EVI*10000", "Peak_DOY", "Pheno_NObs", "GrowingDOY"] if qa: n_bands += 1 band_names.append("SegmentQAQC") logger.debug("Allocating memory") raster = np.ones((image_ds.RasterYSize, image_ds.RasterXSize, n_bands), dtype=np.int32) * int(ndv) logger.debug("Processing results") for rec in iter_records(records, warn_on_empty=WARN_ON_EMPTY): if not all([_attr in rec.dtype.names for _attr in attributes]): raise ValueError("Results do not have phenology metrics") for _qa, index in find_indices(rec, date, after=after, before=before): if index.shape[0] == 0: continue # Apply scale factors for R and peak EVI rec["pheno_cor"][index] *= 10000.0 rec["peak_evi"][index] *= 10000.0 for _b, _attr in enumerate(attributes): raster[rec["py"][index], rec["px"][index], _b] = rec[_attr][index] raster[rec["py"][index], rec["px"][index], 6] = rec["autumn_doy"][index] - rec["spring_doy"][index] if qa: raster[rec["py"][index], rec["px"][index], -1] = _qa return raster, band_names
def get_numchangemap(start, end, result_location, image_ds, ndv=-9999, pattern=_result_record): """ Output raster with changemap Args: start (int): Ordinal date for start of map records end (int): Ordinal date for end of map records result_location (str): Location of results image_ds (gdal.Dataset): Example dataset ndv (int, optional): NoDataValue pattern (str, optional): filename pattern of saved record results Returns: np.ndarray: 2D numpy array containing the number of changes between the start and end date; list containing band names """ # Find results records = find_results(result_location, pattern) logger.debug('Allocating memory...') raster = np.ones((image_ds.RasterYSize, image_ds.RasterXSize), dtype=np.int32) * int(ndv) logger.debug('Processing results') for rec in iter_records(records): # X location of each changed model px_changed = rec['px'][(rec['break'] >= start) & (rec['break'] <= end)] # Count occurrences of changed pixel locations bincount = np.bincount(px_changed) # How many changes for unique values of px_changed? n_change = bincount[np.nonzero(bincount)[0]] # Add in the values px = np.unique(px_changed) py = rec['py'][np.in1d(px, rec['px'])] raster[py, px] = n_change return raster
def find_result_attributes(cfg): """ Return result attributes relevant for training a classifier At this time, the only relevant information is the design information. Args: cfg (dict): YATSM configuration dictionary Returns: dict: dictionary of result attributes. Includes 'design_info' key. """ attrs = {"design_info": None} results = utils.find_results(cfg["dataset"]["output"], cfg["dataset"]["output_prefix"] + "*") for result in results: try: res = np.load(result) attrs["design_info"] = res["design_matrix"].item() except: pass else: return attrs raise AttributeError("Could not find following attributes in results: %s" % attrs.keys())
def get_prediction( date, result_location, image_ds, bands="all", use_robust=False, after=False, before=False, qa=False, ndv=-9999, pattern=_result_record, ): """ Output a raster with the predictions from model fit for a given date Args: date (int): Ordinal date for prediction image result_location (str): Location of the results image_ds (gdal.Dataset): Example dataset bands (str, list): Bands to predict - 'all' for every band, or specify a list of bands use_robust (bool, optional): Map robust coefficients and RMSE instead of normal ones after (bool, optional): If date intersects a disturbed period, use next available time segment before (bool, optional): If date does not intersect a model, use previous non-disturbed time segment qa (bool, optional): Add QA flag specifying segment type (intersect, after, or before) ndv (int, optional): NoDataValue pattern (str, optional): filename pattern of saved record results Returns: np.ndarray: A 3D numpy.ndarray containing the prediction for each band, for each pixel """ # Find results records = find_results(result_location, pattern) # Find result attributes to extract i_bands, _, _, _, design, design_info = find_result_attributes(records, bands, None, use_robust=use_robust) n_bands = len(i_bands) band_names = ["Band_{0}".format(b) for b in range(n_bands)] if qa: n_bands += 1 band_names.append("SegmentQAQC") n_i_bands = len(i_bands) # Create X matrix from date -- ignoring categorical variables if re.match(r".*C\(.*\).*", design): logger.warning("Categorical variable found in design matrix not used" " in predicted image estimate") design = re.sub(r"[\+\-][\ ]+C\(.*\)", "", design) X = patsy.dmatrix(design, {"x": date}).squeeze() i_coef = [] for k, v in design_info.iteritems(): if not re.match("C\(.*\)", k): i_coef.append(v) i_coef = np.asarray(i_coef) logger.debug("Allocating memory") raster = np.ones((image_ds.RasterYSize, image_ds.RasterXSize, n_bands), dtype=np.int16) * int(ndv) logger.debug("Processing results") for rec in iter_records(records, warn_on_empty=WARN_ON_EMPTY): for _qa, index in find_indices(rec, date, after=after, before=before): if index.shape[0] == 0: continue # Calculate prediction _coef = rec["coef"].take(index, axis=0).take(i_coef, axis=1).take(i_bands, axis=2) raster[rec["py"][index], rec["px"][index], :n_i_bands] = np.tensordot(_coef, X, axes=(1, 0)) if qa: raster[rec["py"][index], rec["px"][index], -1] = _qa return raster, band_names
def get_coefficients( date, result_location, image_ds, bands, coefs, use_robust=False, amplitude=False, after=False, before=False, qa=False, ndv=-9999, pattern=_result_record, ): """ Output a raster with coefficients from CCDC Args: date (int): Ordinal date for prediction image result_location (str): Location of the results bands (list): Bands to predict coefs (list): List of coefficients to output image_ds (gdal.Dataset): Example dataset use_robust (bool, optional): Map robust coefficients and RMSE instead of normal ones amplitude (bool, optional): Map amplitude of seasonality instead of individual coefficient estimates for sin/cosine pair (default: False) after (bool, optional): If date intersects a disturbed period, use next available time segment (default: False) before (bool, optional): If date does not intersect a model, use previous non-disturbed time segment (default: False) qa (bool, optional): Add QA flag specifying segment type (intersect, after, or before) (default: False) ndv (int, optional): NoDataValue (default: -9999) pattern (str, optional): filename pattern of saved record results Returns: tuple: A tuple (np.ndarray, list) containing the 3D numpy.ndarray of the coefficients (coefficient x band x pixel), and the band names for the output dataset """ # Find results records = find_results(result_location, pattern) # Find result attributes to extract i_bands, i_coefs, use_rmse, coef_names, _, _ = find_result_attributes(records, bands, coefs, use_robust=use_robust) # Process amplitude transform for seasonality coefficients if amplitude: harm_coefs = [] for i, (c, n) in enumerate(zip(i_coefs, coef_names)): if re.match(r"harm\(x, [0-9]+\)\[0]", n): harm_coefs.append(c) coef_names[i] = re.sub(r"harm(.*)\[.*", r"amplitude\1", n) # Remove sin term from each harmonic pair i_coefs = [c for c in i_coefs if c - 1 not in harm_coefs] coef_names = [n for n in coef_names if "harm" not in n] n_bands = len(i_bands) n_coefs = len(i_coefs) n_rmse = n_bands if use_rmse else 0 # Setup output band names band_names = [] for _c in coef_names: for _b in i_bands: band_names.append("B" + str(_b + 1) + "_" + _c.replace(" ", "")) if use_rmse is True: for _b in i_bands: band_names.append("B" + str(_b + 1) + "_RMSE") n_qa = 0 if qa: n_qa += 1 band_names.append("SegmentQAQC") n_out_bands = n_bands * n_coefs + n_rmse + n_qa _coef = "robust_coef" if use_robust else "coef" _rmse = "robust_rmse" if use_robust else "rmse" logger.debug("Allocating memory...") raster = np.ones((image_ds.RasterYSize, image_ds.RasterXSize, n_out_bands), dtype=np.float32) * ndv logger.debug("Processing results") for rec in iter_records(records, warn_on_empty=WARN_ON_EMPTY): for _qa, index in find_indices(rec, date, after=after, before=before): if index.shape[0] == 0: continue if n_coefs > 0: # Normalize intercept to mid-point in time segment rec[_coef][index, 0, :] += ((rec["start"][index] + rec["end"][index]) / 2.0)[:, np.newaxis] * rec[ _coef ][index, 1, :] # If we want amplitude, calculate it if amplitude: for harm_coef in harm_coefs: rec[_coef][index, harm_coef, :] = np.linalg.norm( rec[_coef][index, harm_coef : harm_coef + 2, :], axis=1 ) # Extract coefficients raster[rec["py"][index], rec["px"][index], : n_coefs * n_bands] = np.reshape( rec[_coef][index][:, i_coefs, :][:, :, i_bands], (index.size, n_coefs * n_bands) ) if use_rmse: raster[rec["py"][index], rec["px"][index], n_coefs * n_bands : n_out_bands - n_qa] = rec[_rmse][index][ :, i_bands ] if qa: raster[rec["py"][index], rec["px"][index], -1] = _qa return raster, band_names
def get_classification( date, result_location, image_ds, after=False, before=False, qa=False, pred_proba=False, ndv=0, pattern=_result_record, ): """ Output raster with classification results Args: date (int): ordinal date for prediction image result_location (str): Location of the results image_ds (gdal.Dataset): Example dataset after (bool, optional): If date intersects a disturbed period, use next available time segment before (bool, optional): If date does not intersect a model, use previous non-disturbed time segment qa (bool, optional): Add QA flag specifying segment type (intersect, after, or before) pred_proba (bool, optional): Include additional band with classification value probabilities ndv (int, optional): NoDataValue pattern (str, optional): filename pattern of saved record results Returns: np.ndarray: 2D numpy array containing the classification map for the date specified """ # Find results records = find_results(result_location, pattern) n_bands = 2 if pred_proba else 1 dtype = np.uint16 if pred_proba else np.uint8 band_names = ["Classification"] if pred_proba: band_names.append("Pred Proba (x10,000)") if qa: n_bands += 1 band_names.append("SegmentQAQC") logger.debug("Allocating memory...") raster = np.ones((image_ds.RasterYSize, image_ds.RasterXSize, n_bands), dtype=dtype) * int(ndv) logger.debug("Processing results") for rec, fname in iter_records(records, warn_on_empty=WARN_ON_EMPTY, yield_filename=True): if "class" not in rec.dtype.names: logger.warning("Results in {f} do not have classification labels".format(f=fname)) continue if "class_proba" not in rec.dtype.names and pred_proba: raise ValueError("Results do not have classification prediction" " probability values") for _qa, index in find_indices(rec, date, after=after, before=before): if index.shape[0] == 0: continue raster[rec["py"][index], rec["px"][index], 0] = rec["class"][index] if pred_proba: raster[rec["py"][index], rec["px"][index], 1] = rec["class_proba"][index].max(axis=1) * 10000 if qa: raster[rec["py"][index], rec["px"][index], -1] = _qa return raster, band_names
def get_datechangemap(start, end, result_location, image_ds, first=False, out_format='%Y%j', magnitude=False, ndv=-9999, pattern=_result_record): """ Output raster with changemap Args: start (int): Ordinal date for start of map records end (int): Ordinal date for end of map records result_location (str): Location of results image_ds (gdal.Dataset): Example dataset first (bool): Use first change instead of last out_format (str, optional): Output date format magnitude (bool, optional): output magnitude of each change? ndv (int, optional): NoDataValue pattern (str, optional): filename pattern of saved record results Returns: tuple: A 2D np.ndarray array containing the changes between the start and end date. Also includes, if specified, a 3D np.ndarray of the magnitude of each change plus the indices of these magnitudes """ # Find results records = find_results(result_location, pattern) logger.debug('Allocating memory...') datemap = np.ones((image_ds.RasterYSize, image_ds.RasterXSize), dtype=np.int32) * int(ndv) # Determine what magnitude information to output if requested if magnitude: magnitude_indices = get_magnitude_indices(records) magnitudemap = np.ones((image_ds.RasterYSize, image_ds.RasterXSize, magnitude_indices.size), dtype=np.float32) * float(ndv) logger.debug('Processing results') for rec in iter_records(records): index = np.where((rec['break'] >= start) & (rec['break'] <= end))[0] if first: _, _index = np.unique(rec['px'][index], return_index=True) index = index[_index] if index.shape[0] != 0: if out_format != 'ordinal': dates = np.array([int(dt.fromordinal(_d).strftime(out_format)) for _d in rec['break'][index]]) datemap[rec['py'][index], rec['px'][index]] = dates else: datemap[rec['py'][index], rec['px'][index]] = \ rec['break'][index] if magnitude: magnitudemap[rec['py'][index], rec['px'][index], :] = \ rec[index]['magnitude'][:, magnitude_indices] if magnitude: return datemap, magnitudemap, magnitude_indices else: return datemap, None, None
def get_phenology(date, result_location, image_ds, after=False, before=False, qa=False, ndv=-9999, pattern=_result_record): """ Output a raster containing phenology information Phenology information includes spring_doy, autumn_doy, pheno_cor, peak_evi, peak_doy, and pheno_nobs. Args: date (int): Ordinal date for prediction image result_location (str): Location of the results image_ds (gdal.Dataset): Example dataset after (bool, optional): If date intersects a disturbed period, use next available time segment before (bool, optional): If date does not intersect a model, use previous non-disturbed time segment qa (bool, optional): Add QA flag specifying segment type (intersect, after, or before) ndv (int, optional): NoDataValue pattern (str, optional): filename pattern of saved record results Returns: tuple (np.ndarray, list): A tuple (np.ndarray, list) containing the 3D np.ndarray of the phenology metrics, and the band names for the output dataset """ # Find results records = find_results(result_location, pattern) n_bands = 7 attributes = [ 'spring_doy', 'autumn_doy', 'pheno_cor', 'peak_evi', 'peak_doy', 'pheno_nobs' ] band_names = [ 'SpringDOY', 'AutumnDOY', 'Pheno_R*10000', 'Peak_EVI*10000', 'Peak_DOY', 'Pheno_NObs', 'GrowingDOY' ] if qa: n_bands += 1 band_names.append('SegmentQAQC') logger.debug('Allocating memory') raster = np.ones((image_ds.RasterYSize, image_ds.RasterXSize, n_bands), dtype=np.int32) * int(ndv) logger.debug('Processing results') for rec in iter_records(records, warn_on_empty=WARN_ON_EMPTY): if not all([_attr in rec.dtype.names for _attr in attributes]): raise ValueError('Results do not have phenology metrics') for _qa, index in find_indices(rec, date, after=after, before=before): if index.shape[0] == 0: continue # Apply scale factors for R and peak EVI rec['pheno_cor'][index] *= 10000.0 rec['peak_evi'][index] *= 10000.0 for _b, _attr in enumerate(attributes): raster[rec['py'][index], rec['px'][index], _b] = rec[_attr][index] raster[rec['py'][index], rec['px'][index], 6] = \ rec['autumn_doy'][index] - rec['spring_doy'][index] if qa: raster[rec['py'][index], rec['px'][index], -1] = _qa return raster, band_names
def get_prediction(date, result_location, image_ds, bands='all', use_robust=False, after=False, before=False, qa=False, ndv=-9999, pattern=_result_record): """ Output a raster with the predictions from model fit for a given date Args: date (int): Ordinal date for prediction image result_location (str): Location of the results image_ds (gdal.Dataset): Example dataset bands (str, list): Bands to predict - 'all' for every band, or specify a list of bands use_robust (bool, optional): Map robust coefficients and RMSE instead of normal ones after (bool, optional): If date intersects a disturbed period, use next available time segment before (bool, optional): If date does not intersect a model, use previous non-disturbed time segment qa (bool, optional): Add QA flag specifying segment type (intersect, after, or before) ndv (int, optional): NoDataValue pattern (str, optional): filename pattern of saved record results Returns: np.ndarray: A 3D numpy.ndarray containing the prediction for each band, for each pixel """ # Find results records = find_results(result_location, pattern) # Find result attributes to extract i_bands, _, _, _, design, design_info = find_result_attributes( records, bands, None, use_robust=use_robust) n_bands = len(i_bands) band_names = ['Band_{0}'.format(b) for b in range(n_bands)] if qa: n_bands += 1 band_names.append('SegmentQAQC') n_i_bands = len(i_bands) # Create X matrix from date -- ignoring categorical variables if re.match(r'.*C\(.*\).*', design): logger.warning('Categorical variable found in design matrix not used' ' in predicted image estimate') design = re.sub(r'[\+\-][\ ]+C\(.*\)', '', design) X = patsy.dmatrix(design, {'x': date}).squeeze() i_coef = [] for k, v in design_info.iteritems(): if not re.match('C\(.*\)', k): i_coef.append(v) i_coef = np.asarray(i_coef) logger.debug('Allocating memory') raster = np.ones((image_ds.RasterYSize, image_ds.RasterXSize, n_bands), dtype=np.int16) * int(ndv) logger.debug('Processing results') for rec in iter_records(records, warn_on_empty=WARN_ON_EMPTY): for _qa, index in find_indices(rec, date, after=after, before=before): if index.shape[0] == 0: continue # Calculate prediction _coef = rec['coef'].take(index, axis=0).\ take(i_coef, axis=1).take(i_bands, axis=2) raster[rec['py'][index], rec['px'][index], :n_i_bands] = \ np.tensordot(_coef, X, axes=(1, 0)) if qa: raster[rec['py'][index], rec['px'][index], -1] = _qa return raster, band_names
def get_coefficients(date, result_location, image_ds, bands, coefs, use_robust=False, amplitude=False, after=False, before=False, qa=False, ndv=-9999, pattern=_result_record): """ Output a raster with coefficients from CCDC Args: date (int): Ordinal date for prediction image result_location (str): Location of the results bands (list): Bands to predict coefs (list): List of coefficients to output image_ds (gdal.Dataset): Example dataset use_robust (bool, optional): Map robust coefficients and RMSE instead of normal ones amplitude (bool, optional): Map amplitude of seasonality instead of individual coefficient estimates for sin/cosine pair (default: False) after (bool, optional): If date intersects a disturbed period, use next available time segment (default: False) before (bool, optional): If date does not intersect a model, use previous non-disturbed time segment (default: False) qa (bool, optional): Add QA flag specifying segment type (intersect, after, or before) (default: False) ndv (int, optional): NoDataValue (default: -9999) pattern (str, optional): filename pattern of saved record results Returns: tuple: A tuple (np.ndarray, list) containing the 3D numpy.ndarray of the coefficients (coefficient x band x pixel), and the band names for the output dataset """ # Find results records = find_results(result_location, pattern) # Find result attributes to extract i_bands, i_coefs, use_rmse, coef_names, _, _ = find_result_attributes( records, bands, coefs, use_robust=use_robust) # Process amplitude transform for seasonality coefficients if amplitude: harm_coefs = [] for i, (c, n) in enumerate(zip(i_coefs, coef_names)): if re.match(r'harm\(x, [0-9]+\)\[0]', n): harm_coefs.append(c) coef_names[i] = re.sub(r'harm(.*)\[.*', r'amplitude\1', n) # Remove sin term from each harmonic pair i_coefs = [c for c in i_coefs if c - 1 not in harm_coefs] coef_names = [n for n in coef_names if 'harm' not in n] n_bands = len(i_bands) n_coefs = len(i_coefs) n_rmse = n_bands if use_rmse else 0 # Setup output band names band_names = [] for _c in coef_names: for _b in i_bands: band_names.append('B' + str(_b + 1) + '_' + _c.replace(' ', '')) if use_rmse is True: for _b in i_bands: band_names.append('B' + str(_b + 1) + '_RMSE') n_qa = 0 if qa: n_qa += 1 band_names.append('SegmentQAQC') n_out_bands = n_bands * n_coefs + n_rmse + n_qa _coef = 'robust_coef' if use_robust else 'coef' _rmse = 'robust_rmse' if use_robust else 'rmse' logger.debug('Allocating memory...') raster = np.ones((image_ds.RasterYSize, image_ds.RasterXSize, n_out_bands), dtype=np.float32) * ndv logger.debug('Processing results') for rec in iter_records(records, warn_on_empty=WARN_ON_EMPTY): for _qa, index in find_indices(rec, date, after=after, before=before): if index.shape[0] == 0: continue if n_coefs > 0: # Normalize intercept to mid-point in time segment rec[_coef][index, 0, :] += ( (rec['start'][index] + rec['end'][index]) / 2.0)[:, np.newaxis] * \ rec[_coef][index, 1, :] # If we want amplitude, calculate it if amplitude: for harm_coef in harm_coefs: rec[_coef][index, harm_coef, :] = np.linalg.norm( rec[_coef][index, harm_coef:harm_coef + 2, :], axis=1) # Extract coefficients raster[rec['py'][index], rec['px'][index], :n_coefs * n_bands] =\ np.reshape(rec[_coef][index][:, i_coefs, :][:, :, i_bands], (index.size, n_coefs * n_bands)) if use_rmse: raster[rec['py'][index], rec['px'][index], n_coefs * n_bands:n_out_bands - n_qa] =\ rec[_rmse][index][:, i_bands] if qa: raster[rec['py'][index], rec['px'][index], -1] = _qa return raster, band_names
def get_classification(date, result_location, image_ds, after=False, before=False, qa=False, pred_proba=False, ndv=0, pattern=_result_record): """ Output raster with classification results Args: date (int): ordinal date for prediction image result_location (str): Location of the results image_ds (gdal.Dataset): Example dataset after (bool, optional): If date intersects a disturbed period, use next available time segment before (bool, optional): If date does not intersect a model, use previous non-disturbed time segment qa (bool, optional): Add QA flag specifying segment type (intersect, after, or before) pred_proba (bool, optional): Include additional band with classification value probabilities ndv (int, optional): NoDataValue pattern (str, optional): filename pattern of saved record results Returns: np.ndarray: 2D numpy array containing the classification map for the date specified """ # Find results records = find_results(result_location, pattern) n_bands = 2 if pred_proba else 1 dtype = np.uint16 if pred_proba else np.uint8 band_names = ['Classification'] if pred_proba: band_names.append('Pred Proba (x10,000)') if qa: n_bands += 1 band_names.append('SegmentQAQC') logger.debug('Allocating memory...') raster = np.ones((image_ds.RasterYSize, image_ds.RasterXSize, n_bands), dtype=dtype) * int(ndv) logger.debug('Processing results') for rec, fname in iter_records(records, warn_on_empty=WARN_ON_EMPTY, yield_filename=True): if 'class' not in rec.dtype.names: logger.warning( 'Results in {f} do not have classification labels'.format( f=fname)) continue if 'class_proba' not in rec.dtype.names and pred_proba: raise ValueError('Results do not have classification prediction' ' probability values') for _qa, index in find_indices(rec, date, after=after, before=before): if index.shape[0] == 0: continue raster[rec['py'][index], rec['px'][index], 0] = rec['class'][index] if pred_proba: raster[rec['py'][index], rec['px'][index], 1] = \ rec['class_proba'][index].max(axis=1) * 10000 if qa: raster[rec['py'][index], rec['px'][index], -1] = _qa return raster, band_names
def get_coefficients( date, result_location, image_ds, bands, coefs, no_scale_intercept=False, use_robust=False, after=False, before=False, qa=False, ndv=-9999, pattern=_result_record, ): """ Output a raster with coefficients from CCDC Args: date (int): Ordinal date for prediction image result_location (str): Location of the results bands (list): Bands to predict coefs (list): List of coefficients to output image_ds (gdal.Dataset): Example dataset no_scale_intercept (bool, optional): Skip scaling of intercept coefficient by slope (default: False) use_robust (bool, optional): Map robust coefficients and RMSE instead of normal ones after (bool, optional): If date intersects a disturbed period, use next available time segment before (bool, optional): If date does not intersect a model, use previous non-disturbed time segment qa (bool, optional): Add QA flag specifying segment type (intersect, after, or before) ndv (int, optional): NoDataValue pattern (str, optional): filename pattern of saved record results Returns: tuple: A tuple (np.ndarray, list) containing the 3D numpy.ndarray of the coefficients (coefficient x band x pixel), and the band names for the output dataset """ # Find results records = find_results(result_location, pattern) # Find result attributes to extract i_bands, i_coefs, use_rmse, coef_names, _, _ = find_result_attributes(records, bands, coefs, use_robust=use_robust) n_bands = len(i_bands) n_coefs = len(i_coefs) n_rmse = n_bands if use_rmse else 0 # Setup output band names band_names = [] for _c in coef_names: for _b in i_bands: band_names.append("B" + str(_b + 1) + "_" + _c.replace(" ", "")) if use_rmse is True: for _b in i_bands: band_names.append("B" + str(_b + 1) + "_RMSE") n_qa = 0 if qa: n_qa += 1 band_names.append("SegmentQAQC") n_out_bands = n_bands * n_coefs + n_rmse + n_qa logger.debug("Band names:") logger.debug(band_names) _coef = "robust_coef" if use_robust else "coef" _rmse = "robust_rmse" if use_robust else "rmse" logger.debug("Allocating memory...") raster = np.ones((image_ds.RasterYSize, image_ds.RasterXSize, n_out_bands), dtype=np.float32) * ndv logger.debug("Processing results") for rec in iter_records(records, warn_on_empty=WARN_ON_EMPTY): for _qa, index in find_indices(rec, date, after=after, before=before): if index.shape[0] == 0: continue if n_coefs > 0: # Normalize intercept to mid-point in time segment if not no_scale_intercept: rec[_coef][index, 0, :] += ((rec["start"][index] + rec["end"][index]) / 2.0)[:, None] * rec[_coef][ index, 1, : ] # Extract coefficients raster[rec["py"][index], rec["px"][index], : n_coefs * n_bands] = np.reshape( rec[_coef][index][:, i_coefs, :][:, :, i_bands], (index.size, n_coefs * n_bands) ) if use_rmse: raster[rec["py"][index], rec["px"][index], n_coefs * n_bands : n_out_bands - n_qa] = rec[_rmse][index][ :, i_bands ] if qa: raster[rec["py"][index], rec["px"][index], -1] = _qa return raster, band_names
def get_datechangemap(start, end, result_location, image_ds, first=False, out_format='%Y%j', magnitude=False, ndv=-9999, pattern=_result_record): """ Output raster with changemap Args: start (int): Ordinal date for start of map records end (int): Ordinal date for end of map records result_location (str): Location of results image_ds (gdal.Dataset): Example dataset first (bool): Use first change instead of last out_format (str, optional): Output date format magnitude (bool, optional): output magnitude of each change? ndv (int, optional): NoDataValue pattern (str, optional): filename pattern of saved record results Returns: tuple: A 2D np.ndarray array containing the changes between the start and end date. Also includes, if specified, a 3D np.ndarray of the magnitude of each change plus the indices of these magnitudes """ # Find results records = find_results(result_location, pattern) logger.debug('Allocating memory...') datemap = np.ones((image_ds.RasterYSize, image_ds.RasterXSize), dtype=np.int32) * int(ndv) # Determine what magnitude information to output if requested if magnitude: magnitude_indices = get_magnitude_indices(records) magnitudemap = np.ones((image_ds.RasterYSize, image_ds.RasterXSize, magnitude_indices.size), dtype=np.float32) * float(ndv) logger.debug('Processing results') for rec in iter_records(records): index = np.where((rec['break'] >= start) & (rec['break'] <= end))[0] if first: _, _index = np.unique(rec['px'][index], return_index=True) index = index[_index] if index.shape[0] != 0: if out_format != 'ordinal': dates = np.array([ int(dt.fromordinal(_d).strftime(out_format)) for _d in rec['break'][index] ]) datemap[rec['py'][index], rec['px'][index]] = dates else: datemap[rec['py'][index], rec['px'][index]] = \ rec['break'][index] if magnitude: magnitudemap[rec['py'][index], rec['px'][index], :] = \ rec[index]['magnitude'][:, magnitude_indices] if magnitude: return datemap, magnitudemap, magnitude_indices else: return datemap, None, None