def make_polygon(lons, lats, name, author, tags): fc = FeatureCollection() coords = list() for index in range(len(lons)): coords.append([lons[index], lats[index]]) coords.append([lons[0], lats[0]]) fc.add_feature({ "type": "Feature", "properties": { "name": name, "author": author, "object": 'region', "component": 'ocean', "tags": tags, "zmin": -1500., "zmax": -200. }, "geometry": { "type": "Polygon", "coordinates": [coords] } }) return fc
def get_longest_contour(contourValue, author): def stereo_to_lon_lat(x, y): return pyproj.transform(inProj, outProj, x, y) ds = xarray.open_dataset('bedmap2.nc') # plot contours plt.figure() cs = plt.contour(ds.x.values, ds.y.values, ds.bed, (contourValue, )) paths = cs.collections[0].get_paths() pathLengths = [len(paths[i]) for i in range(len(paths))] iLongest = numpy.argmax(pathLengths) p = paths[iLongest] v = p.vertices x = v[:, 0] y = v[:, 1] # Antarctic stereographic inProj = pyproj.Proj(init='epsg:3031') # lon/lat outProj = pyproj.Proj(init='epsg:4326') lon, lat = pyproj.transform(inProj, outProj, x, y) poly = shapely.geometry.Polygon([(i[0], i[1]) for i in zip(x, y)]) epsilon = 1e-14 minY = numpy.amin(y) wedge = shapely.geometry.Polygon([(epsilon, minY), (epsilon**2, -epsilon), (0, epsilon), (-epsilon**2, -epsilon), (-epsilon, minY), (epsilon, minY)]) difference = poly.difference(wedge) difference = shapely.ops.transform(stereo_to_lon_lat, difference) x, y = difference.exterior.xy plt.figure() plt.plot(x, y) fc = FeatureCollection() geometry = shapely.geometry.mapping(difference) # get rid of the wedge again by rounding the coordinates geometry['coordinates'] = _round_coords(geometry['coordinates']) fc.add_feature({ "type": "Feature", "properties": { "name": "Contour {}".format(contourValue), "author": author, "object": 'region', "component": 'ocean' }, "geometry": geometry }) return fc
def remove_small_polygons(fc, minArea): ''' A helper function to remove small polygons from a feature collection Parameters ---------- fc : ``FeatureCollection`` The feature collection to remove polygons from minArea : float The minimum area (in square degrees) below which polygons should be removed Returns ------- fcOut : ``FeatureCollection`` The new feature collection with small polygons removed ''' # Authors # ------- # Xylar Asay-Davis fcOut = FeatureCollection() removedCount = 0 for feature in fc.features: geom = feature['geometry'] if geom['type'] not in ['Polygon', 'MultiPolygon']: # no area to check, so just add it fcOut.add_feature(copy.deepcopy(feature)) else: add = False featureShape = shapely.geometry.shape(geom) if featureShape.type == 'Polygon': if featureShape.area > minArea: add = True else: removedCount += 1 else: # a MultiPolygon outPolygons = [] for polygon in featureShape: if polygon.area > minArea: outPolygons.append(polygon) else: removedCount += 1 if len(outPolygons) > 0: outShape = shapely.ops.cascaded_union(outPolygons) feature['geometry'] = shapely.geometry.mapping(outShape) add = True if add: fcOut.add_feature(copy.deepcopy(feature)) else: print("{} has been removed.".format( feature['pproperties']['name'])) print(' * Removed {} small polygons'.format(removedCount)) return fcOut
def make_rectangle(lon0, lon1, lat0, lat1, name, author, tags): fc = FeatureCollection() fc.add_feature( {"type": "Feature", "properties": {"name": name, "author": author, "object": 'region', "component": 'ocean', "tags": tags}, "geometry": { "type": "Polygon", "coordinates": [[[lon0, lat0], [lon1, lat0], [lon1, lat1], [lon0, lat1], [lon0, lat0]]]}}) return fc
def run_task(self): # {{{ """ Plots time-series output of properties in an ocean region. """ # Authors # ------- # Xylar Asay-Davis self.logger.info("\nPlotting TS diagram for {}" "...".format(self.regionName)) register_custom_colormaps() config = self.config sectionName = self.sectionName startYear = self.mpasClimatologyTask.startYear endYear = self.mpasClimatologyTask.endYear regionMaskSuffix = config.getExpression(sectionName, 'regionMaskSuffix') regionMaskFile = get_region_mask(config, '{}.geojson'.format(regionMaskSuffix)) fcAll = read_feature_collection(regionMaskFile) fc = FeatureCollection() for feature in fcAll.features: if feature['properties']['name'] == self.regionName: fc.add_feature(feature) break self.logger.info(' Make plots...') groupLink = 'tsDiag' + self.regionGroup[0].lower() + \ self.regionGroup[1:].replace(' ', '') nSubplots = 1 + len(self.obsDicts) if self.controlConfig is not None: nSubplots += 1 if nSubplots == 4: nCols = 2 nRows = 2 else: nCols = min(nSubplots, 3) nRows = (nSubplots - 1) // 3 + 1 axisIndices = numpy.reshape(numpy.arange(nRows * nCols), (nRows, nCols))[::-1, :].ravel() titleFontSize = config.get('plot', 'titleFontSize') axis_font = {'size': config.get('plot', 'axisFontSize')} title_font = { 'size': titleFontSize, 'color': config.get('plot', 'titleFontColor'), 'weight': config.get('plot', 'titleFontWeight') } width = 3 + 4.5 * nCols height = 2 + 4 * nRows # noinspection PyTypeChecker fig, axarray = plt.subplots(nrows=nRows, ncols=nCols, sharey=True, figsize=(width, height)) if nSubplots == 1: axarray = numpy.array(axarray) if nRows == 1: axarray = axarray.reshape((nRows, nCols)) T, S, zMid, volume, zmin, zmax = self._get_mpas_t_s(self.config) mainRunName = config.get('runs', 'mainRunName') plotFields = [{ 'S': S, 'T': T, 'z': zMid, 'vol': volume, 'title': mainRunName }] if self.controlConfig is not None: T, S, zMid, volume, _, _ = self._get_mpas_t_s(self.controlConfig) controlRunName = self.controlConfig.get('runs', 'mainRunName') plotFields.append({ 'S': S, 'T': T, 'z': zMid, 'vol': volume, 'title': 'Control: {}'.format(controlRunName) }) for obsName in self.obsDicts: obsT, obsS, obsZ, obsVol = self._get_obs_t_s( self.obsDicts[obsName]) plotFields.append({ 'S': obsS, 'T': obsT, 'z': obsZ, 'vol': obsVol, 'title': obsName }) Tbins = config.getExpression(sectionName, 'Tbins', usenumpyfunc=True) Sbins = config.getExpression(sectionName, 'Sbins', usenumpyfunc=True) normType = config.get(sectionName, 'normType') PT, SP = numpy.meshgrid(Tbins, Sbins) SA = gsw.SA_from_SP(SP, p=0., lon=0., lat=-75.) CT = gsw.CT_from_t(SA, PT, p=0.) neutralDensity = sigma0(SA, CT) rhoInterval = config.getfloat(sectionName, 'rhoInterval') contours = numpy.arange(24., 29. + rhoInterval, rhoInterval) diagramType = config.get(sectionName, 'diagramType') if diagramType not in ['volumetric', 'scatter']: raise ValueError('Unexpected diagramType {}'.format(diagramType)) lastPanel = None volMinMpas = None volMaxMpas = None for index in range(len(axisIndices)): panelIndex = axisIndices[index] row = nRows - 1 - index // nCols col = numpy.mod(index, nCols) if panelIndex >= nSubplots: plt.delaxes(axarray[row, col]) continue plt.sca(axarray[row, col]) T = plotFields[index]['T'] S = plotFields[index]['S'] z = plotFields[index]['z'] volume = plotFields[index]['vol'] title = plotFields[index]['title'] CS = plt.contour(SP, PT, neutralDensity, contours, linewidths=1., colors='k', zorder=2) plt.clabel(CS, fontsize=12, inline=1, fmt='%4.2f') if diagramType == 'volumetric': lastPanel, volMin, volMax = \ self._plot_volumetric_panel(T, S, volume) if index == 0: volMinMpas = volMin volMaxMpas = volMax if normType == 'linear': norm = colors.Normalize(vmin=0., vmax=volMaxMpas) elif normType == 'log': if volMinMpas is None or volMaxMpas is None: norm = None else: norm = colors.LogNorm(vmin=volMinMpas, vmax=volMaxMpas) else: raise ValueError( 'Unsupported normType {}'.format(normType)) if norm is not None: lastPanel.set_norm(norm) else: lastPanel = self._plot_scatter_panel(T, S, z, zmin, zmax) CTFreezing = freezing.CT_freezing(Sbins, 0, 1) PTFreezing = gsw.t_from_CT(gsw.SA_from_SP(Sbins, p=0., lon=0., lat=-75.), CTFreezing, p=0.) plt.plot(Sbins, PTFreezing, linestyle='--', linewidth=1., color='k') plt.ylim([Tbins[0], Tbins[-1]]) plt.xlim([Sbins[0], Sbins[-1]]) plt.xlabel('Salinity (PSU)', **axis_font) if col == 0: plt.ylabel(r'Potential temperature ($^\circ$C)', **axis_font) plt.title(title) # do this before the inset because otherwise it moves the inset # and cartopy doesn't play too well with tight_layout anyway plt.tight_layout() fig.subplots_adjust(right=0.91) if nRows == 1: fig.subplots_adjust(top=0.85) else: fig.subplots_adjust(top=0.88) suptitle = 'T-S diagram for {} ({}, {:04d}-{:04d})\n' \ ' {} m < z < {} m'.format(self.regionName, self.season, startYear, endYear, zmin, zmax) fig.text(0.5, 0.9, suptitle, horizontalalignment='center', **title_font) inset = add_inset(fig, fc, width=1.5, height=1.5) # move the color bar down a little ot avoid the inset pos0 = inset.get_position() pos1 = axarray[-1, -1].get_position() pad = 0.04 top = pos0.y0 - pad height = top - pos1.y0 cbar_ax = fig.add_axes([0.92, pos1.y0, 0.02, height]) cbar = fig.colorbar(lastPanel, cax=cbar_ax) if diagramType == 'volumetric': cbar.ax.get_yaxis().labelpad = 15 cbar.ax.set_ylabel(r'volume (m$^3$)', rotation=270) else: cbar.ax.set_ylabel('depth (m)', rotation=270) outFileName = '{}/TS_diagram_{}_{}.png'.format(self.plotsDirectory, self.prefix, self.season) savefig(outFileName, tight=False) caption = 'Regional mean of {}'.format(suptitle) write_image_xml(config=config, filePrefix='TS_diagram_{}_{}'.format( self.prefix, self.season), componentName='Ocean', componentSubdirectory='ocean', galleryGroup='T-S Diagrams', groupLink=groupLink, gallery=self.regionGroup, thumbnailDescription=self.regionName, imageDescription=caption, imageCaption=caption)
def run_task(self): # {{{ """ Plots time-series output of properties in an ocean region. """ # Authors # ------- # Xylar Asay-Davis self.logger.info("\nPlotting time series of ocean properties of {}" "...".format(self.regionName)) self.logger.info(' Load time series...') config = self.config calendar = self.calendar regionMaskSuffix = config.getExpression(self.sectionName, 'regionMaskSuffix') regionMaskFile = get_region_mask(config, '{}.geojson'.format(regionMaskSuffix)) fcAll = read_feature_collection(regionMaskFile) fc = FeatureCollection() for feature in fcAll.features: if feature['properties']['name'] == self.regionName: fc.add_feature(feature) break baseDirectory = build_config_full_path(config, 'output', 'timeSeriesSubdirectory') startYear = config.getint('timeSeries', 'startYear') endYear = config.getint('timeSeries', 'endYear') regionGroup = self.regionGroup timeSeriesName = regionGroup[0].lower() + \ regionGroup[1:].replace(' ', '') inFileName = '{}/{}/{}_{:04d}-{:04d}.nc'.format( baseDirectory, timeSeriesName, timeSeriesName, startYear, endYear) dsIn = xarray.open_dataset(inFileName).isel(nRegions=self.regionIndex) zbounds = dsIn.zbounds.values controlConfig = self.controlConfig plotControl = controlConfig is not None if plotControl: controlRunName = controlConfig.get('runs', 'mainRunName') baseDirectory = build_config_full_path(controlConfig, 'output', 'timeSeriesSubdirectory') startYear = controlConfig.getint('timeSeries', 'startYear') endYear = controlConfig.getint('timeSeries', 'endYear') inFileName = '{}/{}/{}_{:04d}-{:04d}.nc'.format( baseDirectory, timeSeriesName, timeSeriesName, startYear, endYear) dsRef = xarray.open_dataset(inFileName).isel( nRegions=self.regionIndex) zboundsRef = dsRef.zbounds.values mainRunName = config.get('runs', 'mainRunName') movingAverageMonths = 1 self.logger.info(' Make plots...') groupLink = self.regionGroup[0].lower() + \ self.regionGroup[1:].replace(' ', '') for var in self.variables: varName = var['name'] mainArray = dsIn[varName] is3d = mainArray.attrs['is3d'] == 'True' if is3d: title = 'Volume-Mean {} in {}'.format(var['title'], self.regionName) else: title = 'Area-Mean {} in {}'.format(var['title'], self.regionName) if plotControl: refArray = dsRef[varName] xLabel = 'Time (yr)' yLabel = '{} ({})'.format(var['title'], var['units']) filePrefix = '{}_{}'.format(self.prefix, varName) outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) fields = [mainArray] lineColors = ['k'] lineWidths = [2.5] legendText = [mainRunName] if plotControl: fields.append(refArray) lineColors.append('r') lineWidths.append(1.2) legendText.append(controlRunName) if is3d: if not plotControl or numpy.all(zbounds == zboundsRef): title = '{} ({} < z < {} m)'.format( title, zbounds[0], zbounds[1]) else: legendText[0] = '{} ({} < z < {} m)'.format( legendText[0], zbounds[0], zbounds[1]) legendText[1] = '{} ({} < z < {} m)'.format( legendText[1], zboundsRef[0], zboundsRef[1]) fig = timeseries_analysis_plot( config, fields, calendar=calendar, title=title, xlabel=xLabel, ylabel=yLabel, movingAveragePoints=movingAverageMonths, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText) # do this before the inset because otherwise it moves the inset # and cartopy doesn't play too well with tight_layout anyway plt.tight_layout() add_inset(fig, fc, width=2.0, height=2.0) savefig(outFileName, tight=False) caption = 'Regional mean of {}'.format(title) write_image_xml(config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='{} Time Series'.format( self.regionGroup), groupLink=groupLink, gallery=var['title'], thumbnailDescription=self.regionName, imageDescription=caption, imageCaption=caption)
def run_task(self): # {{{ """ Plot a depth profile with variability """ # Authors # ------- # Xylar Asay-Davis config = self.config startYear = self.startYear endYear = self.endYear regionMaskFile = self.masksSubtask.geojsonFileName fcAll = read_feature_collection(regionMaskFile) fc = FeatureCollection() for feature in fcAll.features: if feature['properties']['name'] == self.regionName: fc.add_feature(feature) break inDirectory = build_config_full_path(config, 'output', 'profilesSubdirectory') timeSeriesName = self.timeSeriesName inFileName = '{}/{}_{}_{:04d}-{:04d}.nc'.format( inDirectory, timeSeriesName, self.season, self.startYear, self.endYear) regionGroup = self.masksSubtask.regionGroup regionGroupSection = 'profiles{}'.format( regionGroup.replace(' ', '')) ds = xr.open_dataset(inFileName) allRegionNames = decode_strings(ds.regionNames) regionIndex = allRegionNames.index(self.regionName) ds = ds.isel(nRegions=regionIndex) meanFieldName = '{}_mean'.format(self.field['prefix']) stdFieldName = '{}_std'.format(self.field['prefix']) mainRunName = config.get('runs', 'mainRunName') profileGalleryGroup = config.get(regionGroupSection, 'profileGalleryGroup') titleFieldName = self.field['titleName'] regionName = self.regionName.replace('_', ' ') xLabel = '{} ({})'.format(titleFieldName, self.field['units']) yLabel = 'depth (m)' outFileName = '{}/{}.png'.format(self.plotsDirectory, self.filePrefix) lineColors = ['k'] lineWidths = [1.6] zArrays = [ds.z.values] fieldArrays = [ds[meanFieldName].values] errArrays = [ds[stdFieldName].values] if self.controlConfig is None: title = '{} {}, years {:04d}-{:04d}\n{}'.format( regionName, self.season, startYear, endYear, mainRunName) legendText = [None] else: controlStartYear = self.controlConfig.getint('climatology', 'startYear') controlEndYear = self.controlConfig.getint('climatology', 'endYear') controlRunName = self.controlConfig.get('runs', 'mainRunName') if controlStartYear == startYear and controlEndYear == endYear: title = '{} {}, years {:04d}-{:04d}'.format( regionName, self.season, startYear, endYear) legendText = [mainRunName, controlRunName] elif mainRunName == controlRunName: title = '{} {}\n{}'.format( regionName, self.season, mainRunName) legendText = ['{:04d}-{:04d}'.format(startYear, endYear), '{:04d}-{:04d}'.format(controlStartYear, controlEndYear)] else: title = '{} {} '.format(regionName, self.season) legendText = ['{} {:04d}-{:04d}'.format(mainRunName, startYear, endYear), '{} {:04d}-{:04d}'.format(controlRunName, controlStartYear, controlEndYear)] controlDirectory = build_config_full_path( self.controlConfig, 'output', 'profilesSubdirectory') controlFileName = \ '{}/{}_{}_{:04d}-{:04d}.nc'.format( controlDirectory, timeSeriesName, self.season, controlStartYear, controlEndYear) dsControl = xr.open_dataset(controlFileName) allRegionNames = decode_strings(dsControl.regionNames) regionIndex = allRegionNames.index(self.regionName) dsControl = dsControl.isel(nRegions=regionIndex) lineColors.append('r') lineWidths.append(1.2) zArrays.append(dsControl.z.values) fieldArrays.append(dsControl[meanFieldName].values) errArrays.append(dsControl[stdFieldName].values) depthRange = config.getExpression(regionGroupSection, 'depthRange') if len(depthRange) == 0: depthRange = None fig = self.plot(zArrays, fieldArrays, errArrays, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, title=title, xLabel=xLabel, yLabel=yLabel, yLim=depthRange) # do this before the inset because otherwise it moves the inset # and cartopy doesn't play too well with tight_layout anyway plt.tight_layout() add_inset(fig, fc, width=1.0, height=1.0) savefig(outFileName, tight=False) caption = '{} {} vs depth'.format(regionName, titleFieldName) write_image_xml( config=config, filePrefix=self.filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup=profileGalleryGroup, groupLink='ocnregprofs', imageDescription=caption, imageCaption=caption, gallery=titleFieldName, thumbnailDescription='{} {}'.format(regionName, self.season))
iceMask = numpy.logical_or(iceMask, bedMask) groundedMask = numpy.logical_or(groundedMask, bedMask) masks = dict() masks['AntarcticIceCoverage'] = iceMask masks['AntarcticGroundedIceCoverage'] = groundedMask fc = FeatureCollection() for name in masks: properties = dict() properties['name'] = name properties['component'] = 'bedmachine' properties['author'] = \ 'Morlighem et al. (2019) doi:10.1038/s41561-019-0510-8' properties['object'] = 'region' properties['tags'] = '' feature = dict() feature['properties'] = properties feature['geometry'] = extract_geometry(masks[name]) fc.add_feature(feature) fc.to_geojson(out_file_name) gf = GeometricFeatures(cacheLocation='./geometric_data') gf.split(fc) write_feature_names_and_tags(gf.cacheLocation) os.rename('features_and_tags.json', 'geometric_features/features_and_tags.json')
def run_task(self): # {{{ """ Plots time-series output of Antarctic sub-ice-shelf melt rates. """ # Authors # ------- # Xylar Asay-Davis, Stephen Price self.logger.info("\nPlotting Antarctic melt rate time series for " "{}...".format(self.iceShelf)) self.logger.info(' Load melt rate data...') config = self.config calendar = self.calendar iceShelfMasksFile = self.iceShelfMasksFile fcAll = read_feature_collection(iceShelfMasksFile) fc = FeatureCollection() for feature in fcAll.features: if feature['properties']['name'] == self.iceShelf: fc.add_feature(feature) break totalMeltFlux, meltRates = self._load_ice_shelf_fluxes(config) plotControl = self.controlConfig is not None if plotControl: controlRunName = self.controlConfig.get('runs', 'mainRunName') refTotalMeltFlux, refMeltRates = \ self._load_ice_shelf_fluxes(self.controlConfig) # Load observations from multiple files and put in dictionary based # on shelf keyname observationsDirectory = build_obs_path(config, 'ocean', 'meltSubdirectory') obsFileNameDict = {'Rignot et al. (2013)': 'Rignot_2013_melt_rates_20200623.csv', 'Rignot et al. (2013) SS': 'Rignot_2013_melt_rates_SS_20200623.csv'} obsDict = {} # dict for storing dict of obs data for obsName in obsFileNameDict: obsFileName = '{}/{}'.format(observationsDirectory, obsFileNameDict[obsName]) obsDict[obsName] = {} obsFile = csv.reader(open(obsFileName, 'rU')) next(obsFile, None) # skip the header line for line in obsFile: # some later useful values commented out shelfName = line[0] if shelfName != self.iceShelf: continue # surveyArea = line[1] meltFlux = float(line[2]) meltFluxUncertainty = float(line[3]) meltRate = float(line[4]) meltRateUncertainty = float(line[5]) # actualArea = float( line[6] ) # actual area here is in sq km # build dict of obs. keyed to filename description # (which will be used for plotting) obsDict[obsName] = { 'meltFlux': meltFlux, 'meltFluxUncertainty': meltFluxUncertainty, 'meltRate': meltRate, 'meltRateUncertainty': meltRateUncertainty} break # If areas from obs file used need to be converted from sq km to sq m mainRunName = config.get('runs', 'mainRunName') movingAverageMonths = config.getint('timeSeriesAntarcticMelt', 'movingAverageMonths') outputDirectory = build_config_full_path(config, 'output', 'timeseriesSubdirectory') make_directories(outputDirectory) self.logger.info(' Make plots...') # get obs melt flux and unc. for shelf (similar for rates) obsMeltFlux = [] obsMeltFluxUnc = [] obsMeltRate = [] obsMeltRateUnc = [] for obsName in obsDict: if len(obsDict[obsName]) > 0: obsMeltFlux.append( obsDict[obsName]['meltFlux']) obsMeltFluxUnc.append( obsDict[obsName]['meltFluxUncertainty']) obsMeltRate.append( obsDict[obsName]['meltRate']) obsMeltRateUnc.append( obsDict[obsName]['meltRateUncertainty']) else: # append NaN so this particular obs won't plot self.logger.warning('{} observations not available for ' '{}'.format(obsName, self.iceShelf)) obsMeltFlux.append(None) obsMeltFluxUnc.append(None) obsMeltRate.append(None) obsMeltRateUnc.append(None) title = self.iceShelf.replace('_', ' ') xLabel = 'Time (yr)' yLabel = 'Melt Flux (GT/yr)' timeSeries = totalMeltFlux.isel(nRegions=self.regionIndex) filePrefix = 'melt_flux_{}'.format(self.iceShelf.replace(' ', '_')) outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) fields = [timeSeries] lineColors = ['k'] lineWidths = [2.5] legendText = [mainRunName] if plotControl: fields.append(refTotalMeltFlux.isel(nRegions=self.regionIndex)) lineColors.append('r') lineWidths.append(1.2) legendText.append(controlRunName) fig = timeseries_analysis_plot(config, fields, calendar=calendar, title=title, xlabel=xLabel, ylabel=yLabel, movingAveragePoints=movingAverageMonths, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, obsMean=obsMeltFlux, obsUncertainty=obsMeltFluxUnc, obsLegend=list(obsDict.keys())) # do this before the inset because otherwise it moves the inset # and cartopy doesn't play too well with tight_layout anyway plt.tight_layout() add_inset(fig, fc, width=2.0, height=2.0) savefig(outFileName) caption = 'Running Mean of Total Melt Flux under Ice ' \ 'Shelves in the {} Region'.format(title) write_image_xml( config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Antarctic Melt Time Series', groupLink='antmelttime', gallery='Total Melt Flux', thumbnailDescription=title, imageDescription=caption, imageCaption=caption) xLabel = 'Time (yr)' yLabel = 'Melt Rate (m/yr)' timeSeries = meltRates.isel(nRegions=self.regionIndex) filePrefix = 'melt_rate_{}'.format(self.iceShelf.replace(' ', '_')) outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) fields = [timeSeries] lineColors = ['k'] lineWidths = [2.5] legendText = [mainRunName] if plotControl: fields.append(refMeltRates.isel(nRegions=self.regionIndex)) lineColors.append('r') lineWidths.append(1.2) legendText.append(controlRunName) if config.has_option(self.taskName, 'firstYearXTicks'): firstYearXTicks = config.getint(self.taskName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(self.taskName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(self.taskName, 'yearStrideXTicks') else: yearStrideXTicks = None fig = timeseries_analysis_plot(config, fields, calendar=calendar, title=title, xlabel=xLabel, ylabel=yLabel, movingAveragePoints=movingAverageMonths, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks, obsMean=obsMeltRate, obsUncertainty=obsMeltRateUnc, obsLegend=list(obsDict.keys())) # do this before the inset because otherwise it moves the inset # and cartopy doesn't play too well with tight_layout anyway plt.tight_layout() add_inset(fig, fc, width=2.0, height=2.0) savefig(outFileName) caption = 'Running Mean of Area-averaged Melt Rate under Ice ' \ 'Shelves in the {} Region'.format(title) write_image_xml( config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Antarctic Melt Time Series', groupLink='antmelttime', gallery='Area-averaged Melt Rate', thumbnailDescription=title, imageDescription=caption, imageCaption=caption)
def run_task(self): # {{{ """ Make the Hovmoller plot from the time series. """ # Authors # ------- # Xylar Asay-Davis, Milena Veneziani, Greg Streletz self.logger.info("\nPlotting {} time series vs. depth...".format( self.fieldNameInTitle)) config = self.config mainRunName = config.get('runs', 'mainRunName') self.logger.info(' Load ocean data...') ds = xr.open_dataset(self.inFileName) if 'regionNames' in ds.coords: allRegionNames = decode_strings(ds.regionNames) regionIndex = allRegionNames.index(self.regionName) regionNameInTitle = self.regionName.replace('_', ' ') regionDim = ds.regionNames.dims[0] else: plotTitles = config.getExpression('regions', 'plotTitles') allRegionNames = config.getExpression('regions', 'regions') regionIndex = allRegionNames.index(self.regionName) regionNameInTitle = plotTitles[regionIndex] regionDim = 'nOceanRegionsTmp' ds = ds.isel(**{regionDim: regionIndex}) # Note: restart file, not a mesh file because we need refBottomDepth, # not in a mesh file try: restartFile = self.runStreams.readpath('restart')[0] except ValueError: raise IOError('No MPAS-O restart file found: need at least one ' 'restart file for plotting time series vs. depth') # Define/read in general variables self.logger.info(' Read in depth...') with xr.open_dataset(restartFile) as dsRestart: # reference depth [m] depths = dsRestart.refBottomDepth.values z = np.zeros(depths.shape) z[0] = -0.5 * depths[0] z[1:] = -0.5 * (depths[0:-1] + depths[1:]) Time = ds.Time.values field = ds[self.mpasFieldName].values.transpose() xLabel = 'Time (years)' yLabel = 'Depth (m)' title = '{}, {}'.format(self.fieldNameInTitle, regionNameInTitle) outFileName = '{}/{}.png'.format(self.plotsDirectory, self.filePrefix) if config.has_option(self.sectionName, 'firstYearXTicks'): firstYearXTicks = config.getint(self.sectionName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(self.sectionName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(self.sectionName, 'yearStrideXTicks') else: yearStrideXTicks = None movingAverageMonths = config.getWithDefault(self.sectionName, 'movingAverageMonths', 1) if config.has_option(self.sectionName, 'yLim'): yLim = config.getExpression(self.sectionName, 'yLim') else: yLim = None if self.controlConfig is None: refField = None diff = None refTitle = None diffTitle = None else: controlConfig = self.controlConfig dsRef = xr.open_dataset(self.controlFileName) if 'regionNames' in dsRef.coords: allRegionNames = decode_strings(dsRef.regionNames) regionIndex = allRegionNames.index(self.regionName) regionNameInTitle = self.regionName.replace('_', ' ') regionDim = dsRef.regionNames.dims[0] else: plotTitles = controlConfig.getExpression( 'regions', 'plotTitles') allRegionNames = controlConfig.getExpression( 'regions', 'regions') regionIndex = allRegionNames.index(self.regionName) regionNameInTitle = plotTitles[regionIndex] regionDim = 'nOceanRegionsTmp' dsRef = dsRef.isel(**{regionDim: regionIndex}) refField = dsRef[self.mpasFieldName].values.transpose() assert (field.shape == refField.shape) diff = field - refField refTitle = self.controlConfig.get('runs', 'mainRunName') diffTitle = 'Main - Control' fig, _, suptitle = plot_vertical_section_comparison( config, Time, z, field, refField, diff, self.sectionName, colorbarLabel=self.unitsLabel, title=title, modelTitle=mainRunName, refTitle=refTitle, diffTitle=diffTitle, xlabel=xLabel, ylabel=yLabel, lineWidth=1, xArrayIsTime=True, movingAveragePoints=movingAverageMonths, calendar=self.calendar, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks, yLim=yLim, invertYAxis=False) if self.regionMaskFile is not None: # shift the super-title a little to the left to make room for the # inset pos = suptitle.get_position() suptitle.set_position((pos[0] - 0.05, pos[1])) fcAll = read_feature_collection(self.regionMaskFile) fc = FeatureCollection() for feature in fcAll.features: if feature['properties']['name'] == self.regionName: fc.add_feature(feature) break add_inset(fig, fc, width=1.0, height=1.0, xbuffer=0.1, ybuffer=0.1) savefig(outFileName, tight=False) else: savefig(outFileName) write_image_xml(config=config, filePrefix=self.filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup=self.galleryGroup, groupSubtitle=self.groupSubtitle, groupLink=self.groupLink, gallery=self.galleryName, thumbnailDescription='{} {}'.format( regionNameInTitle, self.thumbnailSuffix), imageDescription=self.imageCaption, imageCaption=self.imageCaption)
def run_task(self): # {{{ """ Plots time-series output of transport through transects. """ # Authors # ------- # Xylar Asay-Davis, Stephen Price self.logger.info("\nPlotting time series of transport through " "{}...".format(self.transect)) self.logger.info(' Load transport data...') obsDict = { 'Drake Passage': [120, 175], 'Tasmania-Ant': [147, 167], 'Africa-Ant': None, 'Antilles Inflow': [-23.1, -13.7], 'Mona Passage': [-3.8, -1.4], 'Windward Passage': [-7.2, -6.8], 'Florida-Cuba': [30, 33], 'Florida-Bahamas': [30, 33], 'Indonesian Throughflow': [-21, -11], 'Agulhas': [-90, -50], 'Mozambique Channel': [-20, -8], 'Bering Strait': [0.6, 1.0], 'Lancaster Sound': [-1.0, -0.5], 'Fram Strait': [-4.7, 0.7], 'Davis Strait': [-1.6, -3.6], 'Barents Sea Opening': [1.4, 2.6], 'Nares Strait': [-1.8, 0.2] } config = self.config calendar = self.calendar fcAll = read_feature_collection(self.transportTransectFileName) fc = FeatureCollection() for feature in fcAll.features: if feature['properties']['name'] == self.transect: fc.add_feature(feature) break transport, trans_mean, trans_std = self._load_transport(config) if self.transect in obsDict: bounds = obsDict[self.transect] else: bounds = None plotControl = self.controlConfig is not None mainRunName = config.get('runs', 'mainRunName') movingAverageMonths = config.getint('timeSeriesTransport', 'movingAverageMonths') self.logger.info(' Plotting...') transectName = self.transect.replace('_', ' ') title = transectName thumbnailDescription = transectName xLabel = 'Time (yr)' yLabel = 'Transport (Sv)' filePrefix = 'transport_{}'.format(self.transect.replace(' ', '_')) outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) fields = [transport] lineColors = ['k'] lineWidths = [2.5] meanString = 'mean={:.2f} $\pm$ {:.2f}'.format(trans_mean, trans_std) if plotControl: controlRunName = self.controlConfig.get('runs', 'mainRunName') ref_transport, ref_mean, ref_std = \ self._load_transport(self.controlConfig) refMeanString = 'mean={:.2f} $\pm$ {:.2f}'.format( ref_mean, ref_std) fields.append(ref_transport) lineColors.append('r') lineWidths.append(1.2) legendText = [ '{} ({})'.format(mainRunName, meanString), '{} ({})'.format(controlRunName, refMeanString) ] else: legendText = [mainRunName] title = '{} ({})'.format(title, meanString) if config.has_option(self.taskName, 'firstYearXTicks'): firstYearXTicks = config.getint(self.taskName, 'firstYearXTicks') else: firstYearXTicks = None if config.has_option(self.taskName, 'yearStrideXTicks'): yearStrideXTicks = config.getint(self.taskName, 'yearStrideXTicks') else: yearStrideXTicks = None fig = timeseries_analysis_plot(config, fields, calendar=calendar, title=title, xlabel=xLabel, ylabel=yLabel, movingAveragePoints=movingAverageMonths, lineColors=lineColors, lineWidths=lineWidths, legendText=legendText, firstYearXTicks=firstYearXTicks, yearStrideXTicks=yearStrideXTicks) if bounds is not None: t = transport.Time.values plt.gca().fill_between(t, bounds[0] * numpy.ones_like(t), bounds[1] * numpy.ones_like(t), alpha=0.3, label='observations') plt.legend(loc='lower left') # do this before the inset because otherwise it moves the inset # and cartopy doesn't play too well with tight_layout anyway plt.tight_layout() add_inset(fig, fc, width=2.0, height=2.0) savefig(outFileName) caption = 'Transport through the {} Transect'.format(transectName) write_image_xml(config=config, filePrefix=filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup='Transport Time Series', groupLink='transporttime', thumbnailDescription=thumbnailDescription, imageDescription=caption, imageCaption=caption)
def _plot_transect(self, remappedModelClimatology, remappedRefClimatology): # {{{ """ plotting the transect """ season = self.season config = self.config configSectionName = self.configSectionName mainRunName = config.get('runs', 'mainRunName') # broadcast x and z to have the same dimensions x, z = xr.broadcast(remappedModelClimatology.x, remappedModelClimatology.z) # set lat and lon in case we want to plot versus these quantities lat = remappedModelClimatology.lat lon = remappedModelClimatology.lon # convert x, z, lat, and lon to numpy arrays; make a copy because # they are sometimes read-only (not sure why) x = x.values.copy().transpose() z = z.values.copy().transpose() lat = lat.values.copy().transpose() lon = lon.values.copy().transpose() self.lat = lat self.lon = lon # This will do strange things at the antemeridian but there's little # we can do about that. lon_pm180 = numpy.mod(lon + 180., 360.) - 180. if self.horizontalBounds is not None: mask = numpy.logical_and( remappedModelClimatology.x.values >= self.horizontalBounds[0], remappedModelClimatology.x.values <= self.horizontalBounds[1]) inset_lon = lon_pm180[mask] inset_lat = lat[mask] else: inset_lon = lon_pm180 inset_lat = lat fc = FeatureCollection() fc.add_feature({ "type": "Feature", "properties": { "name": self.transectName, "author": 'Xylar Asay-Davis', "object": 'transect', "component": 'ocean', "tags": '' }, "geometry": { "type": "LineString", "coordinates": list(map(list, zip(inset_lon, inset_lat))) } }) # z is masked out with NaNs in some locations (where there is land) but # this makes pcolormesh unhappy so we'll zero out those locations z[numpy.isnan(z)] = 0. modelOutput = nans_to_numpy_mask( remappedModelClimatology[self.mpasFieldName].values) modelOutput = modelOutput.transpose() if remappedRefClimatology is None: refOutput = None bias = None else: refOutput = remappedRefClimatology[self.refFieldName] dims = refOutput.dims refOutput = nans_to_numpy_mask(refOutput.values) if dims[1] != 'nPoints': assert (dims[0] == 'nPoints') refOutput = refOutput.transpose() bias = modelOutput - refOutput filePrefix = self.filePrefix outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix) title = '{}\n({}, years {:04d}-{:04d})'.format(self.fieldNameInTitle, season, self.startYear, self.endYear) xLabel = 'Distance [km]' yLabel = 'Depth [m]' # define the axis labels and the data to use for the upper # x axis or axes, if such additional axes have been requested upperXAxes = config.get('transects', 'upperXAxes') numUpperTicks = config.getint('transects', 'numUpperTicks') upperXAxisTickLabelPrecision = config.getint( 'transects', 'upperXAxisTickLabelPrecision') self._set_third_x_axis_to_none() if upperXAxes == 'neither': self._set_second_x_axis_to_none() elif upperXAxes == 'lat': self._set_second_x_axis_to_latitude() elif upperXAxes == 'lon': self._set_second_x_axis_to_longitude() elif upperXAxes == 'both': self._set_second_x_axis_to_longitude() self._set_third_x_axis_to_latitude() elif upperXAxes == 'greatestExtent': if self._greatest_extent(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() elif upperXAxes == 'strictlyMonotonic': if self._strictly_monotonic(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() elif upperXAxes == 'mostMonotonic': if self._most_monotonic(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() elif upperXAxes == 'mostStepsInSameDirection': if self._most_steps_in_same_direction(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() elif upperXAxes == 'fewestDirectionChanges': if self._fewest_direction_changes(lat, lon): self._set_second_x_axis_to_latitude() else: self._set_second_x_axis_to_longitude() else: raise ValueError('invalid option for upperXAxes') # get the parameters determining what type of plot to use, # what line styles and line colors to use, and whether and how # to label contours compareAsContours = config.getboolean('transects', 'compareAsContoursOnSinglePlot') contourLineStyle = config.get('transects', 'contourLineStyle') contourLineColor = config.get('transects', 'contourLineColor') comparisonContourLineStyle = config.get('transects', 'comparisonContourLineStyle') comparisonContourLineColor = config.get('transects', 'comparisonContourLineColor') if compareAsContours: labelContours = config.getboolean( 'transects', 'labelContoursOnContourComparisonPlots') else: labelContours = config.getboolean('transects', 'labelContoursOnHeatmaps') contourLabelPrecision = config.getint('transects', 'contourLabelPrecision') # construct a three-panel comparison plot for the transect, or a # single-panel contour comparison plot if compareAsContours is True fig, axes, suptitle = plot_vertical_section_comparison( config, x, z, modelOutput, refOutput, bias, configSectionName, cbarLabel=self.unitsLabel, xlabel=xLabel, ylabel=yLabel, title=title, modelTitle='{}'.format(mainRunName), refTitle=self.refTitleLabel, diffTitle=self.diffTitleLabel, secondXAxisData=self.secondXAxisData, secondXAxisLabel=self.secondXAxisLabel, thirdXAxisData=self.thirdXAxisData, thirdXAxisLabel=self.thirdXAxisLabel, numUpperTicks=numUpperTicks, upperXAxisTickLabelPrecision=upperXAxisTickLabelPrecision, invertYAxis=False, backgroundColor='#918167', xLim=self.horizontalBounds, compareAsContours=compareAsContours, lineStyle=contourLineStyle, lineColor=contourLineColor, comparisonContourLineStyle=comparisonContourLineStyle, comparisonContourLineColor=comparisonContourLineColor, labelContours=labelContours, contourLabelPrecision=contourLabelPrecision) # shift the super-title a little to the left to make room for the inset pos = suptitle.get_position() suptitle.set_position((pos[0] - 0.05, pos[1])) # make a red start axis and green end axis to correspond to the dots # in the inset for ax in axes: ax.spines['left'].set_color('red') ax.spines['right'].set_color('green') ax.spines['left'].set_linewidth(4) ax.spines['right'].set_linewidth(4) add_inset(fig, fc, width=1.5, height=1.5, xbuffer=0.1, ybuffer=0.1) savefig(outFileName, tight=False) caption = '{} {}'.format(season, self.imageCaption) write_image_xml(config, filePrefix, componentName='Ocean', componentSubdirectory='ocean', galleryGroup=self.galleryGroup, groupSubtitle=self.groupSubtitle, groupLink=self.groupLink, gallery=self.galleryName, thumbnailDescription=self.thumbnailDescription, imageDescription=caption, imageCaption=caption)
def get_longest_contour(contourValue, author): def stereo_to_lon_lat(x, y): return pyproj.transform(inProj, outProj, x, y) bathymetryFileName = 'IBCAO_V3_500m_SM.grd.gz' if not os.path.exists(bathymetryFileName): print('Downloading IBCAO bathymetry data...') # download baseURL = 'https://www.ngdc.noaa.gov/mgg/bathymetry/arctic/grids/version3_0' download_files([bathymetryFileName], baseURL, './') print('Decompressing and reading IBCAO bathymetry data...') infile = gzip.open(bathymetryFileName, 'rb') tmp = tempfile.NamedTemporaryFile(delete=False) shutil.copyfileobj(infile, tmp) infile.close() tmp.close() ds = xarray.open_dataset(tmp.name) os.unlink(tmp.name) x = ds.x.values y = ds.y.values z = ds.z.values z[(x==numpy.amin(x)) | (x==numpy.amax(x)) | (y==numpy.amin(y)) | (y==numpy.amax(y))] = 0.0 # plot contours plt.figure() cs = plt.contour(x, y, z, (contourValue,)) paths = cs.collections[0].get_paths() pathLengths = [len(paths[i]) for i in range(len(paths))] iLongest = numpy.argmax(pathLengths) p = paths[iLongest] v = p.vertices x = v[:, 0] y = v[:, 1] # Arctic stereographic inProj = pyproj.Proj(init='epsg:3995') # lon/lat outProj = pyproj.Proj(init='epsg:4326') lon, lat = pyproj.transform(inProj, outProj, x, y) poly = shapely.geometry.Polygon([(i[0], i[1]) for i in zip(x, y)]) epsilon = 1e-14 maxY = numpy.amax(y) wedge = shapely.geometry.Polygon([(epsilon, maxY), (epsilon**2, epsilon), (0, epsilon), (-epsilon**2, epsilon), (-epsilon, maxY), (epsilon, maxY)]) difference = poly.difference(wedge) difference = shapely.ops.transform(stereo_to_lon_lat, difference) fc = FeatureCollection() geometry = shapely.geometry.mapping(difference) # get rid of the wedge again by rounding the coordinates geometry['coordinates'] = _round_coords(geometry['coordinates']) fc.add_feature( {"type": "Feature", "properties": {"name": "Contour {}".format(contourValue), "author": author, "object": 'region', "component": 'ocean'}, "geometry": geometry}) return fc
def main(): author = 'Milena Veneziani' # make a geometric features object that knows about the geometric data # cache up a couple of directories gf = GeometricFeatures(cacheLocation='../../geometric_data') fc = FeatureCollection() # ********* New Hudson Bay (modified feature) ********* # Extract Foxe Basin from Northwestern Passages, and merge it # to old Hudson Bay feature. Also combine Hudson Strait with # Hudson Bay. Note that the created feature needs to be further # edited by hand to eliminate a seem, due to Foxe Basin not # completely aligning with one side of the old Hudson Bay feature # (and this feature is not fixed by the last step in this script # either). fcCAA = gf.read('ocean', 'region', ['Northwestern Passages']) fcbox1 = make_rectangle(lon0=-84.3, lon1=-71.7, lat0=67.1, lat1=70.75, name='Foxe Basin Box 1', author=author, tags='Hudson_Bay;Arctic;Atlantic_Basin') fcbox2 = make_rectangle(lon0=-86., lon1=-71.7, lat0=63.5, lat1=67.1, name='Foxe Basin Box 2', author=author, tags='Hudson_Bay;Arctic;Atlantic_Basin') fcFoxe_tmp = fcbox1.difference(fcCAA) fcFoxe_tmp = fcbox1.difference(fcFoxe_tmp) fcFoxe = fcbox2.difference(fcCAA) fcFoxe = fcbox2.difference(fcFoxe) fcFoxe.merge(fcFoxe_tmp) fcFoxe = fcFoxe.combine('Foxe Basin') fcHudson = gf.read('ocean', 'region', ['Hudson Bay']) fcHudson.merge(gf.read('ocean', 'region', ['Hudson Strait'])) fcHudson.merge(fcFoxe) fcHudson = fcHudson.combine('Hudson Bay') props = fcHudson.features[0]['properties'] props['tags'] = 'Hudson Bay;Arctic;Atlantic_Basin' props['author'] = author fc = fcHudson # ********* New Canadian Archipelago (modified feature) ********* # Remove Foxe Basin from Northwestern Passages and make new # Canadian Archipelago feature fcCAA = fcCAA.difference(fcFoxe) props = fcCAA.features[0]['properties'] props['name'] = 'Canadian Archipelago' props['tags'] = 'Canadian_Archipelago;Arctic;Atlantic_Basin' props['author'] = author fc.merge(fcCAA) # ********* New Barents Sea (modified feature) ********* # NOTE: this is dependent on existence of *old* features; # in particular, the Barentsz_Sea feature will # be removed after this script is applied # Combine Barentsz_Sea and White_Sea into new Barents Sea feature fcBS = gf.read('ocean', 'region', ['Barentsz Sea']) fcBS.merge(gf.read('ocean', 'region', ['White Sea'])) fcBS = fcBS.combine('Barents Sea') props = fcBS.features[0]['properties'] props['tags'] = 'Barents_Sea;Arctic;Arctic_NSIDC;Arctic_Basin' props['author'] = author fc.merge(fcBS) # ********* Kara Sea (unchanged feature) ********* fcKara = gf.read('ocean', 'region', ['Kara Sea']) props = fcKara.features[0]['properties'] props['tags'] = 'Kara_Sea;Arctic;Arctic_NSIDC;Arctic_Basin' fc.merge(fcKara) # ********* New Arctic Ocean (modified feature) ********* # NOTE: this is dependent on existence of *old* features; # in particular, the Arctic_Ocean, Chukchi_Sea, # East_Siberian_Sea, and Laptev_Sea features will # be superseded after this script is applied # Define triangle between Greenland Sea and Arctic_Ocean # (north of Fram Strait) that is not part of any of the # currently defined Arctic features fc_tmp = gf.read('ocean', 'region', ['Arctic Ocean']) fc_tmp.merge(gf.read('ocean', 'region', ['Lincoln Sea'])) fc_tmp.merge(fcBS) fcArctic = make_rectangle(lon0=-36., lon1=20., lat0=86., lat1=79., name='North of Fram Strait', author=author, tags='Arctic_Basin') fcArctic = fcArctic.difference(fc_tmp) # Define full Arctic *but* Barents and Kara Seas fcArctic.merge(gf.read('ocean', 'region', ['Arctic Ocean'])) fcArctic.merge(gf.read('ocean', 'region', ['Laptev Sea'])) fcArctic.merge(gf.read('ocean', 'region', ['East Siberian Sea'])) fcArctic.merge(gf.read('ocean', 'region', ['Chukchi Sea'])) fcArctic.merge(gf.read('ocean', 'region', ['Beaufort Sea'])) fcArctic.merge(gf.read('ocean', 'region', ['Lincoln Sea'])) fcArctic = fcArctic.combine('Arctic Ocean') props = fcArctic.features[0]['properties'] props['tags'] = 'Arctic_Ocean;Arctic;Arctic_Basin' props['author'] = author fc.merge(fcArctic) # ********* Beaufort Gyre (entirely new feature) ********* fcContour300 = get_longest_contour(contourValue=-300., author=author) fcBG_firstTry = make_rectangle(lon0=-170., lon1=-130., lat0=70.5, lat1=80.5, name='Beaufort Gyre', author=author, tags='Beaufort_Gyre;Arctic_Proshutinsky') fcBG = fcBG_firstTry.difference(fcContour300) fcBG = fcBG_firstTry.difference(fcBG) fc.merge(fcBG) # ********* New NSIDC Chukchi Sea (new feature) ********* # Define Chukchi Sea as MASIE region #2 minus intersection with # Beaufort Gyre, and with Bering Strait transect as southern boundary fcChukchi = FeatureCollection() fcChukchi.add_feature( {"type": "Feature", "properties": {"name": 'Chukchi Sea NSIDC', "author": author, "object": 'region', "component": 'ocean', "tags": 'Chukchi_Sea_NSIDC;Arctic_NSIDC'}, "geometry": { "type": "Polygon", "coordinates": [[[-167.15, 65.74], [-168.01, 65.84], [-168.62, 65.91], [-169.43, 66.01], [-170.24, 66.1], [-180., 66.6], [-180., 80.0], [-156.48, 80.0], [-156.65, 65.37], [-167.15, 65.74]]]}}) fcChukchi = fcChukchi.difference(fcBG) fc.merge(fcChukchi) # ********* Beaufort Gyre shelf (entirely new feature) ********* # Define Beaufort Gyre shelf region, minus intersection with Chukchi Sea fcBGshelf_firstTry = make_rectangle(lon0=-170., lon1=-130., lat0=68.0, lat1=80.5, name='Beaufort Gyre Shelf Box', author=author, tags='Beaufort_Gyre_Shelf;Arctic_Proshutinsky') fcBGshelf = fcBGshelf_firstTry.difference(fcContour300) fcBGshelf_secondTry = fcBGshelf_firstTry.difference(fcBG) fcBGshelf_secondTry = fcBGshelf_secondTry.difference(fcBGshelf) fcBGshelf.merge(fcBGshelf_secondTry) fcBGshelf = fcBGshelf.combine('Beaufort Gyre Shelf') fcBGshelf = fcBGshelf.difference(fcChukchi) props = fcBGshelf.features[0]['properties'] props['name'] = 'Beaufort Gyre Shelf' props['author'] = author props['tags'] = 'Beaufort_Gyre_Shelf;Arctic_Proshutinsky' fc.merge(fcBGshelf) # ********* New NSIDC East Siberian Sea (new feature) ********* # Define East Siberian Sea as MASIE region #3 fcESS = FeatureCollection() fcESS = make_rectangle(lon0=180., lon1=145., lat0=67., lat1=80., name='East Siberian Sea NSIDC', author=author, tags='East_Siberian_Sea_NSIDC;Arctic_NSIDC') fc.merge(fcESS) # ********* New NSIDC Laptev Sea (new feature) ********* # Define Laptev Sea as MASIE region #4, minus intersection with # Kara Sea fcLap = FeatureCollection() fcLap.add_feature( {"type": "Feature", "properties": {"name": 'Laptev Sea NSIDC', "author": author, "object": 'region', "component": 'ocean', "tags": 'Laptev_Sea_NSIDC;Arctic_NSIDC'}, "geometry": { "type": "Polygon", "coordinates": [[[145., 68.], [145., 80.], [95.4, 81.29], [99.89, 78.27], [102., 72.], [145., 68.]]]}}) fcLap = fcLap.difference(fcKara) fc.merge(fcLap) # ********* Central Arctic (entirely new feature) ********* # Define Central Arctic region as New Arctic Ocean minus BG, BGshelf, # New Chukchi, New ESS, and New Laptev fcCentralArctic = fcArctic.difference(fcBG) fcCentralArctic = fcCentralArctic.difference(fcBGshelf) fcCentralArctic = fcCentralArctic.difference(fcChukchi) fcCentralArctic = fcCentralArctic.difference(fcESS) fcCentralArctic = fcCentralArctic.difference(fcLap) props = fcCentralArctic.features[0]['properties'] props['name'] = 'Central Arctic' props['tags'] = 'Central_Arctic;Arctic;Arctic_Basin' props['author'] = author fc.merge(fcCentralArctic) # "split" these features into individual files in the geometric data cache gf.split(fc) # update the database of feature names and tags write_feature_names_and_tags(gf.cacheLocation) # move the resulting file into place shutil.copyfile('features_and_tags.json', '../../geometric_features/features_and_tags.json') # Fix features if necessary fcArcticTags = gf.read(componentName='ocean', objectType='region', tags=['Arctic']) for feature in fcArcticTags.features: featureName = feature['properties']['name'] shape = shapely.geometry.shape(feature['geometry']) print('{} is_valid: {}'.format(featureName, shape.is_valid)) if not shape.is_valid: fixed = shape.buffer(0) print(' Fixed? {}'.format(fixed.is_valid)) feature['geometry'] = shapely.geometry.mapping(fixed) fcArcticNSIDCTags = gf.read(componentName='ocean', objectType='region', tags=['Arctic_NSIDC']) for feature in fcArcticNSIDCTags.features: featureName = feature['properties']['name'] shape = shapely.geometry.shape(feature['geometry']) print('{} is_valid: {}'.format(featureName, shape.is_valid)) if not shape.is_valid: fixed = shape.buffer(0) print(' Fixed? {}'.format(fixed.is_valid)) feature['geometry'] = shapely.geometry.mapping(fixed) fcArctic = fcArcticTags fcArctic.merge(fcArcticNSIDCTags) fcArctic.to_geojson('arctic_ocean_regions.geojson') fcArctic.plot(projection='northpole') # "split" these features into individual files in the geometric data cache gf.split(fcArctic) # update the database of feature names and tags write_feature_names_and_tags(gf.cacheLocation) # move the resulting file into place shutil.copyfile('features_and_tags.json', '../../geometric_features/features_and_tags.json') plt.show()
def main(): author = 'Milena Veneziani' # make a geometric features object that knows about the geometric data # cache up a couple of directories gf = GeometricFeatures(cacheLocation='../../geometric_data', remoteBranchOrTag='master') fc = FeatureCollection() # *********** First, fix Atlantic_Basin regions in such a way that ******** # ********** they do not overlap each other (so that, we can combine # ********* them together to form a Atlantic Basin region) # ********* New Baltic Sea (modified feature) ********* # Combine old Baltic Sea feature with other small features fcBalticSea = gf.read('ocean', 'region', ['Baltic Sea']) fcFinland = gf.read('ocean', 'region', ['Gulf of Finland']) fcBothnia = gf.read('ocean', 'region', ['Gulf of Bothnia']) fcRiga = gf.read('ocean', 'region', ['Gulf of Riga']) fcKattegat = gf.read('ocean', 'region', ['Kattegat']) fcSkaggerak = gf.read('ocean', 'region', ['Skaggerak']) fcBalticSea.merge(fcFinland) fcBalticSea.merge(fcBothnia) fcBalticSea.merge(fcRiga) fcBalticSea.merge(fcKattegat) fcBalticSea.merge(fcSkaggerak) fcBalticSea = fcBalticSea.combine('Baltic Sea') props = fcBalticSea.features[0]['properties'] props['tags'] = 'Baltic_Sea;Arctic;Atlantic_Basin' props['author'] = author fc = fcBalticSea # ********* New North Atlantic Ocean (modified feature) ********* # Fix North Atlantic so that it does not overlap with new Labdrador # Sea, Irminger Sea, North Sea, Greenland Sea, and Norwegian Sea fcNorthAtlantic = gf.read('ocean', 'region', ['North Atlantic Ocean']) fcLabSea = gf.read('ocean', 'region', ['Labrador Sea']) fcIrmSea = gf.read('ocean', 'region', ['Irminger Sea']) fcGS = gf.read('ocean', 'region', ['Greenland Sea']) fcNS = gf.read('ocean', 'region', ['Norwegian Sea']) fcNorthSea = gf.read('ocean', 'region', ['North Sea']) fc_todiscard = fcLabSea fc_todiscard.merge(fcIrmSea) fc_todiscard.merge(fcGS) fc_todiscard.merge(fcNS) fc_todiscard.merge(fcNorthSea) fc_todiscard = fc_todiscard.combine('Combined region to discard') fcNorthAtlantic.merge(fc_todiscard) fcNorthAtlantic = fcNorthAtlantic.combine('North Atlantic Ocean') fcNorthAtlantic = fcNorthAtlantic.difference(fc_todiscard) props = fcNorthAtlantic.features[0]['properties'] props['tags'] = 'North_Atlantic_Ocean;Atlantic_Basin' props['author'] = author fc.merge(fcNorthAtlantic) # *********** Second, complete definition of oceanography-relevant ********* # ********** Arctic regions, started in part I, and identified with # ********* tag='Arctic' # ********* New Canadian Archipelago (modified feature) ********* # Modify old CAA by 1) including the shelf in the Arctic Ocean, and # 2) including Nares Strait. Old CAA feature will be superseded by this. fcContour500 = get_longest_contour(contourValue=-500., author=author) fcContour1000 = get_longest_contour(contourValue=-1000., author=author) fcCAA1 = make_rectangle(lon0=-130., lon1=-90., lat0=67.0, lat1=86.0, name='West Canadian Archipelago', author=author, tags='Canadian_Archipelago;Arctic;Atlantic_Basin') fcCAA1 = fcCAA1.difference(fcContour500) fcCAA2 = make_rectangle(lon0=-90., lon1=-70., lat0=67.0, lat1=86.0, name='Mid Canadian Archipelago', author=author, tags='Canadian_Archipelago;Arctic;Atlantic_Basin') fcCAA2 = fcCAA2.difference(fcContour1000) fcCAA3 = make_rectangle(lon0=-70., lon1=-50.5, lat0=76.0, lat1=86.0, name='East Canadian Archipelago', author=author, tags='Canadian_Archipelago;Arctic;Atlantic_Basin') fcCAA3 = fcCAA3.difference(fcContour500) fcCAA = fcCAA1 fcCAA.merge(fcCAA2) fcCAA.merge(fcCAA3) fcCAA = fcCAA.combine('Canadian Archipelago') fcHudson = gf.read('ocean', 'region', ['Hudson Bay']) fcCAA = fcCAA.difference(fcHudson) fcBaffin = gf.read('ocean', 'region', ['Baffin Bay']) fcCAA = fcCAA.difference(fcBaffin) props = fcCAA.features[0]['properties'] props['tags'] = 'Canadian_Archipelago;Arctic;Atlantic_Basin' props['author'] = author fc.merge(fcCAA) # ********* Canada Basin (new feature) ********* # This is a slightly modified version of the Beaufort Gyre feature # defined in part I. Differences include 1) a region that expands to # the west to touch the northern boundary of the new CAA feature, and # 2) a region that includes the Canada Basin shelf (whereas, for the # Beuafort Gyre, we have separated the 'Gyre' from the 'Gyre Shelf'). fcContour300 = get_longest_contour(contourValue=-300., author=author) fcCB1 = make_rectangle(lon0=-170., lon1=-156.65, lat0=67.0, lat1=80.0, name='West Canada Basin', author=author, tags='Canada_Basin;Arctic;Arctic_Basin') fcCB = fcCB1.difference(fcContour300) fcCB = fcCB1.difference(fcCB) fcCB2 = make_rectangle(lon0=-156.65, lon1=-100.0, lat0=67.0, lat1=80.0, name='East Canada Basin', author=author, tags='Canada_Basin;Arctic;Arctic_Basin') fcCB2 = fcCB2.difference(fcCAA) fcCB.merge(fcCB2) fcCB = fcCB.combine('Canada Basin') props = fcCB.features[0]['properties'] props['tags'] = 'Canada_Basin;Arctic;Arctic_Basin' props['author'] = author fc.merge(fcCB) # ********* Chukchi Sea (new feature) ********* # This supersedes the old Chukchi Sea feature fcChukchi = make_rectangle(lon0=-180., lon1=-156.65, lat0=65.0, lat1=80.0, name='Chukchi Sea', author=author, tags='Chukchi_Sea;Arctic;Arctic_Basin') fcChukchi = fcChukchi.difference(fcContour300) fcChukchi_NSIDC = gf.read('ocean', 'region', ['Chukchi Sea NSIDC']) fcChukchi_todiscard = fcChukchi.difference(fcChukchi_NSIDC) fcChukchi = fcChukchi.difference(fcChukchi_todiscard) props = fcChukchi.features[0]['properties'] props['tags'] = 'Chukchi_Sea;Arctic;Arctic_Basin' props['author'] = author fc.merge(fcChukchi) # ********* East Siberian Sea (new feature) ********* # This supersedes the old East Siberian Sea feature fcESS = make_rectangle(lon0=142., lon1=180.0, lat0=68.5, lat1=80.0, name='East Siberian Sea', author=author, tags='East_Siberian_Sea;Arctic;Arctic_Basin') fcESS = fcESS.difference(fcContour300) props = fcESS.features[0]['properties'] props['tags'] = 'East_Siberian_Sea;Arctic;Arctic_Basin' props['author'] = author fc.merge(fcESS) # ********* Laptev Sea (new feature) ********* # This supersedes the old Laptev Sea feature fcLap = make_rectangle(lon0=90., lon1=142.0, lat0=70.0, lat1=81.25, name='Laptev Sea', author=author, tags='Laptev_Sea;Arctic;Arctic_Basin') fcLap = fcLap.difference(fcContour300) fcKara = gf.read('ocean', 'region', ['Kara Sea']) fcLap = fcLap.difference(fcKara) props = fcLap.features[0]['properties'] props['tags'] = 'Laptev_Sea;Arctic;Arctic_Basin' props['author'] = author fc.merge(fcLap) # ********* Central Arctic (new feature) ********* # Define Central Arctic region as Arctic Ocean minus Canadian # Archipelago, Canada Basin, Chukchi Sea, ESS, and Laptev Sea fcArctic = gf.read('ocean', 'region', ['Arctic Ocean']) fcCentralArctic = fcArctic.difference(fcCAA) fcCentralArctic = fcCentralArctic.difference(fcCB) fcCentralArctic = fcCentralArctic.difference(fcChukchi) fcCentralArctic = fcCentralArctic.difference(fcESS) fcCentralArctic = fcCentralArctic.difference(fcLap) props = fcCentralArctic.features[0]['properties'] props['name'] = 'Central Arctic' props['tags'] = 'Central_Arctic;Arctic;Arctic_Basin' props['author'] = author fc.merge(fcCentralArctic) # *********** Third, complete definition of seaice-relevant *********** # ***** Arctic regions, started in part I, according to NSIDC # ***** (regions map: https://nsidc.org/data/masie/browse_regions) # **** and identified with tag='Arctic_NSIDC' # ********* New Chukchi Sea NSIDC (modified feature) ********* # This supersedes the Chukchi Sea NSIDC feature defined in part I fcChukchi_NSIDC = FeatureCollection() fcChukchi_NSIDC.add_feature({ "type": "Feature", "properties": { "name": 'Chukchi Sea NSIDC', "author": author, "object": 'region', "component": 'ocean', "tags": 'Chukchi_Sea_NSIDC;Arctic_NSIDC' }, "geometry": { "type": "Polygon", "coordinates": [[[-156.65, 65.37], [-180.0, 66.0], [-180.0, 80.0], [-156.48, 80.0], [-156.65, 65.37]]] } }) fc.merge(fcChukchi_NSIDC) # ********* Beaufort Sea NSIDC (new feature) ********* fcBS_NSIDC = FeatureCollection() fcBS_NSIDC.add_feature({ "type": "Feature", "properties": { "name": 'Beaufort Sea NSIDC', "author": author, "object": 'region', "component": 'ocean', "tags": 'Beaufort_Sea_NSIDC;Arctic_NSIDC' }, "geometry": { "type": "Polygon", "coordinates": [[[-156.65, 65.37], [-156.48, 80.0], [-112.34, 77.69], [-124.58, 75.67], [-124.0, 65.0], [-156.65, 65.37]]] } }) fc.merge(fcBS_NSIDC) # ********* Canadian Archipelago NSIDC (new feature) ********* fcCAA_NSIDC = FeatureCollection() fcCAA_NSIDC.add_feature({ "type": "Feature", "properties": { "name": 'Canadian Archipelago NSIDC', "author": author, "object": 'region', "component": 'ocean', "tags": 'Canadian_Archipelago_NSIDC;Arctic_NSIDC' }, "geometry": { "type": "Polygon", "coordinates": [[[-103.41, 60.69], [-124.0, 65.0], [-124.58, 75.67], [-112.34, 77.69], [-69.33, 82.67], [-81.21, 71.79], [-83.94, 70.43], [-84.45, 67.27], [-93.04, 65.70], [-103.41, 60.69]]] } }) fc.merge(fcCAA_NSIDC) # ********* Hudson Bay NSIDC (new feature) ********* fcHudson_NSIDC = FeatureCollection() fcHudson_NSIDC.add_feature({ "type": "Feature", "properties": { "name": 'Hudson Bay NSIDC', "author": author, "object": 'region', "component": 'ocean', "tags": 'Hudson_Bay_NSIDC;Arctic_NSIDC' }, "geometry": { "type": "Polygon", "coordinates": [[[-81.24, 49.19], [-103.41, 60.69], [-93.04, 65.70], [-84.45, 67.27], [-83.94, 70.43], [-81.21, 71.79], [-70.70, 66.95], [-70.12, 65.99], [-63.70, 57.35], [-81.24, 49.19]]] } }) fc.merge(fcHudson_NSIDC) # ********* Baffin Bay NSIDC (new feature) ********* fcBaffin_NSIDC = FeatureCollection() fcBaffin_NSIDC.add_feature({ "type": "Feature", "properties": { "name": 'Baffin Bay NSIDC', "author": author, "object": 'region', "component": 'ocean', "tags": 'Baffin_Bay_NSIDC;Arctic_NSIDC' }, "geometry": { "type": "Polygon", "coordinates": [[[-53.20, 42.0], [-68.07, 38.38], [-76.82, 48.01], [-60.85, 54.33], [-63.70, 57.35], [-70.12, 65.99], [-70.70, 66.95], [-81.21, 71.79], [-69.33, 82.67], [-45.0, 60.0], [-45.0, 42.0], [-53.20, 42.0]]] } }) fc.merge(fcBaffin_NSIDC) # ********* Central Arctic NSIDC (new feature) ********* fcCentralArctic_NSIDC = FeatureCollection() fcCentralArctic_NSIDC.add_feature({ "type": "Feature", "properties": { "name": 'Central Arctic NSIDC', "author": author, "object": 'region', "component": 'ocean', "tags": 'Central_Arctic_NSIDC;Arctic_NSIDC' }, "geometry": { "type": "Polygon", "coordinates": [[[180.0, 80.0], [180.0, 90.0], [-69.33, 90.0], [-180.0, 90.0], [-180.0, 80.0], [-156.48, 80.0], [-112.34, 77.69], [-69.33, 82.67], [-51.66, 74.25], [-12.72, 81.41], [18.99, 79.18], [58.68, 81.08], [94.95, 81.08], [145.0, 80.0], [180.0, 80.0]]] } }) fc.merge(fcCentralArctic_NSIDC) # "split" these features into individual files in the geometric data cache gf.split(fc) # update the database of feature names and tags write_feature_names_and_tags(gf.cacheLocation) # move the resulting file into place shutil.copyfile('features_and_tags.json', '../../geometric_features/features_and_tags.json') # Fix features if necessary fcArcticTags = gf.read(componentName='ocean', objectType='region', tags=['Arctic']) for feature in fcArcticTags.features: featureName = feature['properties']['name'] shape = shapely.geometry.shape(feature['geometry']) print('{} is_valid: {}'.format(featureName, shape.is_valid)) if not shape.is_valid: fixed = shape.buffer(0) print(' Fixed? {}'.format(fixed.is_valid)) feature['geometry'] = shapely.geometry.mapping(fixed) fcArcticTags.plot(projection='northpole') fcArcticTags.to_geojson('arctic_ocean_regions.geojson') fcArcticNSIDCTags = gf.read(componentName='ocean', objectType='region', tags=['Arctic_NSIDC']) for feature in fcArcticNSIDCTags.features: featureName = feature['properties']['name'] shape = shapely.geometry.shape(feature['geometry']) print('{} is_valid: {}'.format(featureName, shape.is_valid)) if not shape.is_valid: fixed = shape.buffer(0) print(' Fixed? {}'.format(fixed.is_valid)) feature['geometry'] = shapely.geometry.mapping(fixed) fcArcticNSIDCTags.plot(projection='northpole') fcArcticNSIDCTags.to_geojson('arcticNSIDC_ocean_regions.geojson') fcArctic = fcArcticTags fcArctic.merge(fcArcticNSIDCTags) # "split" these features into individual files in the geometric data cache gf.split(fcArctic) # update the database of feature names and tags write_feature_names_and_tags(gf.cacheLocation) # move the resulting file into place shutil.copyfile('features_and_tags.json', '../../geometric_features/features_and_tags.json') plt.show()