import numpy as np
import gdal
from osgeo import gdal
from osgeo import osr
from osgeo import ogr
from osgeo.gdalconst import *
gdal.AllRegister ( ) # register all drivers
gdal.UseExceptions ( )
'''http://m...content-available-to-author-only...l.com/blog/archive/2012/5/2/understanding-raster-basic-gis-concepts-and-the-python-gdal-library/'''
#############
# Functions #
#############
def transform_utm_to_wgs84( easting, northing, zone) :
utm_coordinate_system = osr.SpatialReference ( )
# Set geographic coordinate system to handle lat/lon
utm_coordinate_system.SetWellKnownGeogCS ( "WGS84" )
is_northern = northing > 0
utm_coordinate_system.SetUTM ( zone, is_northern)
# Clone ONLY the geographic coordinate system
wgs84_coordinate_system = utm_coordinate_system.CloneGeogCS ( )
# create transform component
utm_to_wgs84_geo_transform = osr.CoordinateTransformation ( utm_coordinate_system, wgs84_coordinate_system) # (, )
# returns lon, lat, altitude
return utm_to_wgs84_geo_transform.TransformPoint ( easting, northing, 0 )
class WGS84Transform( object ) :
# TODO decide whether to have init and/or call functions
#def transform_wgs84_to_utm(lon, lat)
# def __init__(self,lon,lat):
# self.lon = lon
# self.lat = lat
def get_utm_zone( self , longitude) :
return ( int ( 1 +( longitude+180.0 ) /6.0 ) )
def is_lat_northern( self , latitude) :
"""
Determines if given latitude is a northern for UTM
"""
if ( latitude < 0.0 ) :
return 0
else :
return 1
def wgs84_to_utm( self , lon, lat) :
utm_coordinate_system = osr.SpatialReference ( )
# Set geographic coordinate system to handle lat/lon
utm_coordinate_system.SetWellKnownGeogCS ( "WGS84" )
utm_coordinate_system.SetUTM ( self .get_utm_zone ( lon) , self .is_lat_northern ( lat) )
# Clone ONLY the geographic coordinate system
wgs84_coordinate_system = utm_coordinate_system.CloneGeogCS ( )
# create transform component
wgs84_to_utm_geo_transform = osr.CoordinateTransformation ( wgs84_coordinate_system, utm_coordinate_system) # (, )
# returns easting, northing, altitude
return wgs84_to_utm_geo_transform.TransformPoint ( lon, lat, 0 )
def get_iterable_extent( *args) :
'''Returns list of minimum and maximum from lists/array input'''
iterable_extent = list ( )
for iter_object in args:
iterable_extent.append ( min ( iter_object) )
iterable_extent.append ( max ( iter_object) )
return iterable_extent
def get_raster_size( min_x, min_y, max_x, max_y, cell_width, cell_height) :
"""Determine the number of rows/columns given the bounds of the point
data and the desired cell size"""
print 'raster min_x:' , min_x
print 'raster max_x:' , max_x
print 'raster min_y:' , min_y
print 'raster max_y:' , max_y
cols = int ( ( max_x - min_x) / cell_width)
rows = int ( ( max_y - min_y) / abs ( cell_height) )
return cols, rows
def lonlat_to_pixel( lon, lat, inverse_geo_transform) :
"""Translates the given lon, lat to the grid pixel coordinates
in data array (zero start)"""
wgs84_object = WGS84Transform( )
# transform to utm
utm_x, utm_y, utm_z = wgs84_object.wgs84_to_utm ( lon, lat)
print 'utm_x:' , utm_x
print 'utm_y:' , utm_y
print 'utm_z:' , utm_z
# apply inverse geo tranform
pixel_x, pixel_y = gdal.ApplyGeoTransform ( inverse_geo_transform, utm_x, utm_y)
print 'pixel_x:' , pixel_x
print 'pixel_y:' , pixel_y
pixel_x = int ( pixel_x) - 1 # adjust to 0 start for array
pixel_y = int ( pixel_y) - 1 # adjust to 0 start for array
return pixel_x, abs ( pixel_y) # y pixel is likly a negative value given geo_transform
def create_raster( lons, lats, values, filename= "test.tiff" , output_format= "GTiff" ) :
"""lon/lat values in WGS84"""
# create empty raster
driver = gdal.GetDriverByName ( output_format)
number_of_bands = 1
band_type = gdal.GDT_Float32
x_rotation = 0
y_rotation = 0
cell_width_meters = 50.0
cell_height_meters = 50.0
# retrieve bounds for point data
min_lon, max_lon, min_lat, max_lat = get_iterable_extent( lons, lats)
print 'min_lon:' , min_lon
print 'max_lon:' , max_lon
print 'min_lat:' , min_lat
print 'max_lat:' , max_lat
# Set geographic coordinate system to handle lat/lon
srs = osr.SpatialReference ( )
srs.SetWellKnownGeogCS ( "WGS84" )
# Set projected coordinate system to handle meters
wgs84_obj = WGS84Transform( )
srs.SetUTM ( wgs84_obj.get_utm_zone ( min_lon) , wgs84_obj.is_lat_northern ( max_lat) )
# create transforms for point conversion
wgs84_coordinate_system = srs.CloneGeogCS ( ) # clone only the geographic coordinate system
wgs84_to_utm_transform = osr.CoordinateTransformation ( wgs84_coordinate_system, srs)
utm_to_wgs84_transform = osr.CoordinateTransformation ( srs, wgs84_coordinate_system)
# convert to UTM
top_left_x, top_left_y, z = wgs84_obj.wgs84_to_utm ( min_lon, max_lat)
lower_right_x, lower_right_y, z = wgs84_obj.wgs84_to_utm ( max_lon, min_lat)
print 'top_left_x:' , top_left_x
print 'top_left_y:' , top_left_y
print 'lower_right_x:' , lower_right_x
print 'lower_right_y:' , lower_right_y
'''get_raster_size(min_x, min_y, max_x, max_y, cell_width, cell_height)'''
cols, rows = get_raster_size( top_left_x, lower_right_y,
lower_right_x, top_left_y,
cell_width_meters, cell_height_meters)
print cols, rows
dataset = driver.Create ( filename, cols, rows, number_of_bands, band_type)
# GeoTransform parameters
# --> need to know the area that will be covered to define the geo tranform
# top left x, w-e pixel resolution, rotation, top left y, rotation, n-s pixel resolution
geo_transform = [ top_left_x, cell_width_meters, x_rotation, top_left_y, y_rotation, -cell_height_meters ] # cell height must be negative (-) to apply image space to map
dataset.SetGeoTransform ( geo_transform)
dataset.SetProjection ( srs.ExportToWkt ( ) )
inverse_geo_transform = gdal.InvGeoTransform ( geo_transform) [ 1 ] # for mapping lat/lon to pixel
# get the empty raster data array
band = dataset.GetRasterBand ( 1 ) # 1 == band index value
data = band.ReadAsArray ( 0 , 0 , cols, rows) .astype ( np.cfloat )
# TODO check that lat/lon don't need to get meshed
# populate array values for output
for lon, lat, value in zip ( lons, lats, values) :
# apply value to array
#TODO figure out why pixel_x is negative
pixel_x, pixel_y = lonlat_to_pixel( lon, lat, inverse_geo_transform)
print pixel_x, pixel_y, data.shape
data[ pixel_x] [ pixel_y] = value
# write the updated data array to file
band.WriteArray ( data, 0 , 0 )
band.SetNoDataValue ( NULL_VALUE)
band.FlushCache ( )
# set dataset to None to "close" file
dataset = None
# TODO have function return pixel values for interpolation
return pixel_x, pixel_y
#################
# Main Function #
#################
if __name__ == '__main__' :
# example coordinates
lat = [ 45.3 , 56.2 , 23.4 ]
lon = [ 134.6 , 128.7 , 111.9 ]
val = [ 3 , 6 , 2 ]
create_raster( lon, lat, val)
aW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBnZGFsCmZyb20gb3NnZW8gaW1wb3J0IGdkYWwKZnJvbSBvc2dlbyBpbXBvcnQgb3NyCmZyb20gb3NnZW8gaW1wb3J0IG9ncgpmcm9tIG9zZ2VvLmdkYWxjb25zdCBpbXBvcnQgKgpnZGFsLkFsbFJlZ2lzdGVyKCkgIyByZWdpc3RlciBhbGwgZHJpdmVycwpnZGFsLlVzZUV4Y2VwdGlvbnMoKQoKJycnaHR0cDovL20uLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLmwuY29tL2Jsb2cvYXJjaGl2ZS8yMDEyLzUvMi91bmRlcnN0YW5kaW5nLXJhc3Rlci1iYXNpYy1naXMtY29uY2VwdHMtYW5kLXRoZS1weXRob24tZ2RhbC1saWJyYXJ5LycnJwoKIyMjIyMjIyMjIyMjIwojIEZ1bmN0aW9ucyAjCiMjIyMjIyMjIyMjIyMKCmRlZiB0cmFuc2Zvcm1fdXRtX3RvX3dnczg0KGVhc3RpbmcsIG5vcnRoaW5nLCB6b25lKToKICAgIHV0bV9jb29yZGluYXRlX3N5c3RlbSA9IG9zci5TcGF0aWFsUmVmZXJlbmNlKCkKCiAgICAjIFNldCBnZW9ncmFwaGljIGNvb3JkaW5hdGUgc3lzdGVtIHRvIGhhbmRsZSBsYXQvbG9uCiAgICB1dG1fY29vcmRpbmF0ZV9zeXN0ZW0uU2V0V2VsbEtub3duR2VvZ0NTKCJXR1M4NCIpIAogICAgaXNfbm9ydGhlcm4gPSBub3J0aGluZyA+IDAgICAgCiAgICB1dG1fY29vcmRpbmF0ZV9zeXN0ZW0uU2V0VVRNKHpvbmUsIGlzX25vcnRoZXJuKQoKICAgICMgQ2xvbmUgT05MWSB0aGUgZ2VvZ3JhcGhpYyBjb29yZGluYXRlIHN5c3RlbSAKICAgIHdnczg0X2Nvb3JkaW5hdGVfc3lzdGVtID0gdXRtX2Nvb3JkaW5hdGVfc3lzdGVtLkNsb25lR2VvZ0NTKCkgCiAgICAKICAgICMgY3JlYXRlIHRyYW5zZm9ybSBjb21wb25lbnQKICAgIHV0bV90b193Z3M4NF9nZW9fdHJhbnNmb3JtID0gb3NyLkNvb3JkaW5hdGVUcmFuc2Zvcm1hdGlvbih1dG1fY29vcmRpbmF0ZV9zeXN0ZW0sIHdnczg0X2Nvb3JkaW5hdGVfc3lzdGVtKSAjICgsICkKCiAgICAjIHJldHVybnMgbG9uLCBsYXQsIGFsdGl0dWRlCiAgICByZXR1cm4gdXRtX3RvX3dnczg0X2dlb190cmFuc2Zvcm0uVHJhbnNmb3JtUG9pbnQoZWFzdGluZywgbm9ydGhpbmcsIDApIAoKY2xhc3MgV0dTODRUcmFuc2Zvcm0ob2JqZWN0KToKICAgICMgVE9ETyBkZWNpZGUgd2hldGhlciB0byBoYXZlIGluaXQgYW5kL29yIGNhbGwgZnVuY3Rpb25zCiAgICAjZGVmIHRyYW5zZm9ybV93Z3M4NF90b191dG0obG9uLCBsYXQpCgojICAgIGRlZiBfX2luaXRfXyhzZWxmLGxvbixsYXQpOgojICAgIHNlbGYubG9uID0gbG9uCiMJc2VsZi5sYXQgPSBsYXQKCiAgICBkZWYgZ2V0X3V0bV96b25lKHNlbGYsbG9uZ2l0dWRlKToKCXJldHVybiAoaW50KDErKGxvbmdpdHVkZSsxODAuMCkvNi4wKSkKCSAgICAKICAgIGRlZiBpc19sYXRfbm9ydGhlcm4oc2VsZixsYXRpdHVkZSk6CgkiIiIKCURldGVybWluZXMgaWYgZ2l2ZW4gbGF0aXR1ZGUgaXMgYSBub3J0aGVybiBmb3IgVVRNCgkiIiIKCWlmIChsYXRpdHVkZSA8IDAuMCk6CgkgICAgcmV0dXJuIDAKCWVsc2U6CgkgICAgcmV0dXJuIDEKCiAgICBkZWYgd2dzODRfdG9fdXRtKHNlbGYsbG9uLGxhdCk6ICAgICAgICAgICAgCgkgICAgdXRtX2Nvb3JkaW5hdGVfc3lzdGVtID0gb3NyLlNwYXRpYWxSZWZlcmVuY2UoKQoJICAgICMgU2V0IGdlb2dyYXBoaWMgY29vcmRpbmF0ZSBzeXN0ZW0gdG8gaGFuZGxlIGxhdC9sb24gIAoJICAgIHV0bV9jb29yZGluYXRlX3N5c3RlbS5TZXRXZWxsS25vd25HZW9nQ1MoIldHUzg0IikgCgkgICAgdXRtX2Nvb3JkaW5hdGVfc3lzdGVtLlNldFVUTShzZWxmLmdldF91dG1fem9uZShsb24pLCBzZWxmLmlzX2xhdF9ub3J0aGVybihsYXQpKQoJICAgCgkgICAgIyBDbG9uZSBPTkxZIHRoZSBnZW9ncmFwaGljIGNvb3JkaW5hdGUgc3lzdGVtICAKCSAgICB3Z3M4NF9jb29yZGluYXRlX3N5c3RlbSA9IHV0bV9jb29yZGluYXRlX3N5c3RlbS5DbG9uZUdlb2dDUygpIAoJICAgIAoJICAgICMgY3JlYXRlIHRyYW5zZm9ybSBjb21wb25lbnQKCSAgICB3Z3M4NF90b191dG1fZ2VvX3RyYW5zZm9ybSA9IG9zci5Db29yZGluYXRlVHJhbnNmb3JtYXRpb24od2dzODRfY29vcmRpbmF0ZV9zeXN0ZW0sIHV0bV9jb29yZGluYXRlX3N5c3RlbSkgIyAoLCApCgkgICAgIyByZXR1cm5zIGVhc3RpbmcsIG5vcnRoaW5nLCBhbHRpdHVkZQoJICAgIHJldHVybiB3Z3M4NF90b191dG1fZ2VvX3RyYW5zZm9ybS5UcmFuc2Zvcm1Qb2ludChsb24sIGxhdCwgMCkgCgpkZWYgZ2V0X2l0ZXJhYmxlX2V4dGVudCgqYXJncyk6CiAgICAnJydSZXR1cm5zIGxpc3Qgb2YgbWluaW11bSBhbmQgbWF4aW11bSBmcm9tIGxpc3RzL2FycmF5IGlucHV0JycnCiAgICBpdGVyYWJsZV9leHRlbnQgPSBsaXN0KCkKICAgIGZvciBpdGVyX29iamVjdCBpbiBhcmdzOgoJaXRlcmFibGVfZXh0ZW50LmFwcGVuZChtaW4oaXRlcl9vYmplY3QpKQoJaXRlcmFibGVfZXh0ZW50LmFwcGVuZChtYXgoaXRlcl9vYmplY3QpKQogICAgcmV0dXJuIGl0ZXJhYmxlX2V4dGVudCAKCgpkZWYgZ2V0X3Jhc3Rlcl9zaXplKG1pbl94LCBtaW5feSwgbWF4X3gsIG1heF95LCBjZWxsX3dpZHRoLCBjZWxsX2hlaWdodCk6CiAgICAiIiJEZXRlcm1pbmUgdGhlIG51bWJlciBvZiByb3dzL2NvbHVtbnMgZ2l2ZW4gdGhlIGJvdW5kcyBvZiB0aGUgcG9pbnQgCiAgICBkYXRhIGFuZCB0aGUgZGVzaXJlZCBjZWxsIHNpemUiIiIKCiAgICBwcmludCAncmFzdGVyIG1pbl94OicsbWluX3ggCiAgICBwcmludCAncmFzdGVyIG1heF94OicsbWF4X3ggCiAgICBwcmludCAncmFzdGVyIG1pbl95OicsbWluX3kgCiAgICBwcmludCAncmFzdGVyIG1heF95OicsbWF4X3kgCiAgICBjb2xzID0gaW50KChtYXhfeCAtIG1pbl94KSAvIGNlbGxfd2lkdGgpCiAgICByb3dzID0gaW50KChtYXhfeSAtIG1pbl95KSAvIGFicyhjZWxsX2hlaWdodCkpCiAgICByZXR1cm4gY29scywgcm93cwoKCmRlZiBsb25sYXRfdG9fcGl4ZWwobG9uLCBsYXQsIGludmVyc2VfZ2VvX3RyYW5zZm9ybSk6CiAgICAiIiJUcmFuc2xhdGVzIHRoZSBnaXZlbiBsb24sIGxhdCB0byB0aGUgZ3JpZCBwaXhlbCBjb29yZGluYXRlcwogICAgaW4gZGF0YSBhcnJheSAoemVybyBzdGFydCkiIiIKCiAgICB3Z3M4NF9vYmplY3QgPSBXR1M4NFRyYW5zZm9ybSgpCiAgICAjIHRyYW5zZm9ybSB0byB1dG0KICAgIHV0bV94LCB1dG1feSwgdXRtX3ogPSB3Z3M4NF9vYmplY3Qud2dzODRfdG9fdXRtKGxvbiwgbGF0KQogICAgcHJpbnQgJ3V0bV94OicsdXRtX3gKICAgIHByaW50ICd1dG1feTonLHV0bV95CiAgICBwcmludCAndXRtX3o6Jyx1dG1fegoKICAgICMgYXBwbHkgaW52ZXJzZSBnZW8gdHJhbmZvcm0KICAgIHBpeGVsX3gsIHBpeGVsX3kgPSBnZGFsLkFwcGx5R2VvVHJhbnNmb3JtKGludmVyc2VfZ2VvX3RyYW5zZm9ybSwgdXRtX3gsIHV0bV95KQogICAgcHJpbnQgJ3BpeGVsX3g6JyxwaXhlbF94CiAgICBwcmludCAncGl4ZWxfeTonLHBpeGVsX3kKICAgIHBpeGVsX3ggPSBpbnQocGl4ZWxfeCkgLSAxICMgYWRqdXN0IHRvIDAgc3RhcnQgZm9yIGFycmF5CiAgICBwaXhlbF95ID0gaW50KHBpeGVsX3kpIC0gMSAjIGFkanVzdCB0byAwIHN0YXJ0IGZvciBhcnJheQoKCiAgICByZXR1cm4gcGl4ZWxfeCwgYWJzKHBpeGVsX3kpICMgeSBwaXhlbCBpcyBsaWtseSBhIG5lZ2F0aXZlIHZhbHVlIGdpdmVuIGdlb190cmFuc2Zvcm0KCgpkZWYgY3JlYXRlX3Jhc3Rlcihsb25zLGxhdHMsdmFsdWVzLGZpbGVuYW1lPSJ0ZXN0LnRpZmYiLG91dHB1dF9mb3JtYXQ9IkdUaWZmIik6ICAgICAgCiAgICAiIiJsb24vbGF0IHZhbHVlcyBpbiBXR1M4NCIiIiAgICAgICAgICAKCiAgICAjIGNyZWF0ZSBlbXB0eSByYXN0ZXIKICAgIGRyaXZlciA9IGdkYWwuR2V0RHJpdmVyQnlOYW1lKG91dHB1dF9mb3JtYXQpCiAgICBudW1iZXJfb2ZfYmFuZHMgPSAxCiAgICBiYW5kX3R5cGUgPSBnZGFsLkdEVF9GbG9hdDMyCiAgIAogICAgeF9yb3RhdGlvbiA9IDAKICAgIHlfcm90YXRpb24gPSAwCiAgICBjZWxsX3dpZHRoX21ldGVycyA9IDUwLjAKICAgIGNlbGxfaGVpZ2h0X21ldGVycyA9IDUwLjAKCiAgICAjIHJldHJpZXZlIGJvdW5kcyBmb3IgcG9pbnQgZGF0YSAgICAgICAgICAgCiAgICBtaW5fbG9uLCBtYXhfbG9uLCBtaW5fbGF0LCBtYXhfbGF0ID0gZ2V0X2l0ZXJhYmxlX2V4dGVudChsb25zLGxhdHMpIAogICAgcHJpbnQgJ21pbl9sb246JyxtaW5fbG9uIAogICAgcHJpbnQgJ21heF9sb246JyxtYXhfbG9uIAogICAgcHJpbnQgJ21pbl9sYXQ6JyxtaW5fbGF0IAogICAgcHJpbnQgJ21heF9sYXQ6JyxtYXhfbGF0IAoKICAgICMgU2V0IGdlb2dyYXBoaWMgY29vcmRpbmF0ZSBzeXN0ZW0gdG8gaGFuZGxlIGxhdC9sb24gICAgICAgIAogICAgc3JzID0gb3NyLlNwYXRpYWxSZWZlcmVuY2UoKQogICAgc3JzLlNldFdlbGxLbm93bkdlb2dDUygiV0dTODQiKQoKICAgICMgU2V0IHByb2plY3RlZCBjb29yZGluYXRlIHN5c3RlbSAgdG8gaGFuZGxlIG1ldGVycyAgICAgICAgIAogICAgd2dzODRfb2JqID0gV0dTODRUcmFuc2Zvcm0oKQogICAgc3JzLlNldFVUTSh3Z3M4NF9vYmouZ2V0X3V0bV96b25lKG1pbl9sb24pLCB3Z3M4NF9vYmouaXNfbGF0X25vcnRoZXJuKG1heF9sYXQpKSAKCiAgICAjIGNyZWF0ZSB0cmFuc2Zvcm1zIGZvciBwb2ludCBjb252ZXJzaW9uCiAgICB3Z3M4NF9jb29yZGluYXRlX3N5c3RlbSA9IHNycy5DbG9uZUdlb2dDUygpICMgY2xvbmUgb25seSB0aGUgZ2VvZ3JhcGhpYyBjb29yZGluYXRlIHN5c3RlbSAgIAogICAgd2dzODRfdG9fdXRtX3RyYW5zZm9ybSA9IG9zci5Db29yZGluYXRlVHJhbnNmb3JtYXRpb24od2dzODRfY29vcmRpbmF0ZV9zeXN0ZW0sIHNycykKICAgIHV0bV90b193Z3M4NF90cmFuc2Zvcm0gPSBvc3IuQ29vcmRpbmF0ZVRyYW5zZm9ybWF0aW9uKHNycywgd2dzODRfY29vcmRpbmF0ZV9zeXN0ZW0pCiAgIAogICAgIyBjb252ZXJ0IHRvIFVUTQogICAgdG9wX2xlZnRfeCwgdG9wX2xlZnRfeSwgeiA9IHdnczg0X29iai53Z3M4NF90b191dG0obWluX2xvbiwgbWF4X2xhdCkgICAgCiAgICBsb3dlcl9yaWdodF94LCBsb3dlcl9yaWdodF95LCB6ID0gd2dzODRfb2JqLndnczg0X3RvX3V0bShtYXhfbG9uLCBtaW5fbGF0KQogICAgcHJpbnQgJ3RvcF9sZWZ0X3g6Jyx0b3BfbGVmdF94CiAgICBwcmludCAndG9wX2xlZnRfeTonLHRvcF9sZWZ0X3kKICAgIHByaW50ICdsb3dlcl9yaWdodF94OicsbG93ZXJfcmlnaHRfeAogICAgcHJpbnQgJ2xvd2VyX3JpZ2h0X3k6Jyxsb3dlcl9yaWdodF95CiAgICAKICAgICcnJ2dldF9yYXN0ZXJfc2l6ZShtaW5feCwgbWluX3ksIG1heF94LCBtYXhfeSwgY2VsbF93aWR0aCwgY2VsbF9oZWlnaHQpJycnCiAgICBjb2xzLCByb3dzID0gZ2V0X3Jhc3Rlcl9zaXplKHRvcF9sZWZ0X3gsbG93ZXJfcmlnaHRfeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG93ZXJfcmlnaHRfeCx0b3BfbGVmdF95LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsX3dpZHRoX21ldGVycyxjZWxsX2hlaWdodF9tZXRlcnMpCiAgICBwcmludCBjb2xzLCByb3dzIAogICAgZGF0YXNldCA9IGRyaXZlci5DcmVhdGUoZmlsZW5hbWUsIGNvbHMsIHJvd3MsIG51bWJlcl9vZl9iYW5kcywgYmFuZF90eXBlKQogICAKICAgICMgR2VvVHJhbnNmb3JtIHBhcmFtZXRlcnMKICAgICMgLS0+IG5lZWQgdG8ga25vdyB0aGUgYXJlYSB0aGF0IHdpbGwgYmUgY292ZXJlZCB0byBkZWZpbmUgdGhlIGdlbyB0cmFuZm9ybQogICAgIyB0b3AgbGVmdCB4LCB3LWUgcGl4ZWwgcmVzb2x1dGlvbiwgcm90YXRpb24sIHRvcCBsZWZ0IHksIHJvdGF0aW9uLCBuLXMgcGl4ZWwgcmVzb2x1dGlvbgogICAgZ2VvX3RyYW5zZm9ybSA9IFsgdG9wX2xlZnRfeCwgY2VsbF93aWR0aF9tZXRlcnMsIHhfcm90YXRpb24sIHRvcF9sZWZ0X3ksIHlfcm90YXRpb24sIC1jZWxsX2hlaWdodF9tZXRlcnMgXSAjIGNlbGwgaGVpZ2h0IG11c3QgYmUgbmVnYXRpdmUgKC0pIHRvIGFwcGx5IGltYWdlIHNwYWNlIHRvIG1hcAogICAgZGF0YXNldC5TZXRHZW9UcmFuc2Zvcm0oZ2VvX3RyYW5zZm9ybSkKICAgIGRhdGFzZXQuU2V0UHJvamVjdGlvbihzcnMuRXhwb3J0VG9Xa3QoKSkKICAgIGludmVyc2VfZ2VvX3RyYW5zZm9ybSA9IGdkYWwuSW52R2VvVHJhbnNmb3JtKGdlb190cmFuc2Zvcm0pWzFdICMgZm9yIG1hcHBpbmcgbGF0L2xvbiB0byBwaXhlbAoKICAgICMgZ2V0IHRoZSBlbXB0eSByYXN0ZXIgZGF0YSBhcnJheQogICAgYmFuZCA9IGRhdGFzZXQuR2V0UmFzdGVyQmFuZCgxKSAjIDEgPT0gYmFuZCBpbmRleCB2YWx1ZQogICAgZGF0YSA9IGJhbmQuUmVhZEFzQXJyYXkoMCwgMCwgY29scywgcm93cykuYXN0eXBlKG5wLmNmbG9hdCkKCiAgICAjIFRPRE8gY2hlY2sgdGhhdCBsYXQvbG9uIGRvbid0IG5lZWQgdG8gZ2V0IG1lc2hlZAogICAgIyBwb3B1bGF0ZSBhcnJheSB2YWx1ZXMgZm9yIG91dHB1dAogICAgZm9yIGxvbiwgbGF0LCB2YWx1ZSBpbiB6aXAobG9ucyxsYXRzLHZhbHVlcyk6CiAgICAgICAgIyBhcHBseSB2YWx1ZSB0byBhcnJheQoJI1RPRE8gZmlndXJlIG91dCB3aHkgcGl4ZWxfeCBpcyBuZWdhdGl2ZQogICAgICAgIHBpeGVsX3gsIHBpeGVsX3kgPSBsb25sYXRfdG9fcGl4ZWwobG9uLCBsYXQsIGludmVyc2VfZ2VvX3RyYW5zZm9ybSkKCXByaW50IHBpeGVsX3gsIHBpeGVsX3ksIGRhdGEuc2hhcGUKICAgICAgICBkYXRhW3BpeGVsX3hdW3BpeGVsX3ldID0gdmFsdWUKICAgICAgIAogICAgIyB3cml0ZSB0aGUgdXBkYXRlZCBkYXRhIGFycmF5IHRvIGZpbGUKICAgIGJhbmQuV3JpdGVBcnJheShkYXRhLCAwLCAwKQogICAgYmFuZC5TZXROb0RhdGFWYWx1ZShOVUxMX1ZBTFVFKQogICAgYmFuZC5GbHVzaENhY2hlKCkKICAgCiAgICAjIHNldCBkYXRhc2V0IHRvIE5vbmUgdG8gImNsb3NlIiBmaWxlCiAgICBkYXRhc2V0ID0gTm9uZQogICAgIyBUT0RPIGhhdmUgZnVuY3Rpb24gcmV0dXJuIHBpeGVsIHZhbHVlcyBmb3IgaW50ZXJwb2xhdGlvbgogICAgcmV0dXJuIHBpeGVsX3gsIHBpeGVsX3kKCiMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbiBGdW5jdGlvbiAjCiMjIyMjIyMjIyMjIyMjIyMjCgppZiBfX25hbWVfXyA9PSAnX19tYWluX18nOgoJIyBleGFtcGxlIGNvb3JkaW5hdGVzCglsYXQgPSBbNDUuMyw1Ni4yLDIzLjRdCglsb24gPSBbMTM0LjYsMTI4LjcsMTExLjldCgl2YWwgPSBbMyw2LDJdCgoJY3JlYXRlX3Jhc3Rlcihsb24sbGF0LHZhbCk=