package btools.mapcreator; import btools.util.ReducedMedianFilter; /** * Container for a srtm-raster + it's meta-data * * @author ab */ public class SrtmRaster { public int ncols; public int nrows; public boolean halfcol; public double xllcorner; public double yllcorner; public double cellsize; public short[] eval_array; public short noDataValue; public boolean usingWeights = false; private boolean missingData = false; public short getElevation( int ilon, int ilat ) { double lon = ilon / 1000000. - 180.; double lat = ilat / 1000000. - 90.; if ( usingWeights ) { return getElevationFromShiftWeights( lon, lat ); } // no weights calculated, use 2d linear interpolation double dcol = (lon - xllcorner)/cellsize -0.5; double drow = (lat - yllcorner)/cellsize -0.5; int row = (int)drow; int col = (int)dcol; if ( col < 0 ) col = 0; if ( col >= ncols-1 ) col = ncols - 2; if ( row < 0 ) row = 0; if ( row >= nrows-1 ) row = nrows - 2; double wrow = drow-row; double wcol = dcol-col; missingData = false; // System.out.println( "wrow=" + wrow + " wcol=" + wcol + " row=" + row + " col=" + col ); double eval = (1.-wrow)*(1.-wcol)*get(row ,col ) + ( wrow)*(1.-wcol)*get(row+1,col ) + (1.-wrow)*( wcol)*get(row ,col+1) + ( wrow)*( wcol)*get(row+1,col+1); // System.out.println( "eval=" + eval ); return missingData ? Short.MIN_VALUE : (short)(eval*4); } private short get( int r, int c ) { short e = eval_array[ (nrows-1-r)*ncols + c ]; if ( e == Short.MIN_VALUE ) missingData = true; return e; } private short getElevationFromShiftWeights( double lon, double lat ) { // calc lat-idx and -weight double alat = lat < 0. ? - lat : lat; alat /= 5.; int latIdx = (int)alat; double wlat = alat - latIdx; double dcol = (lon - xllcorner)/cellsize; double drow = (lat - yllcorner)/cellsize; int row = (int)drow; int col = (int)dcol; double dgx = (dcol-col)*gridSteps; double dgy = (drow-row)*gridSteps; // System.out.println( "wrow=" + wrow + " wcol=" + wcol + " row=" + row + " col=" + col ); int gx = (int)(dgx); int gy = (int)(dgy); double wx = dgx-gx; double wy = dgy-gy; double w00 = (1.-wx)*(1.-wy); double w01 = (1.-wx)*( wy); double w10 = ( wx)*(1.-wy); double w11 = ( wx)*( wy); Weights[][] w0 = getWeights( latIdx ); Weights[][] w1 = getWeights( latIdx+1 ); missingData = false; double m0 = w00*getElevation( w0[gx ][gy ], row, col ) + w01*getElevation( w0[gx ][gy+1], row, col ) + w10*getElevation( w0[gx+1][gy ], row, col ) + w11*getElevation( w0[gx+1][gy+1], row, col ); double m1 = w00*getElevation( w1[gx ][gy ], row, col ) + w01*getElevation( w1[gx ][gy+1], row, col ) + w10*getElevation( w1[gx+1][gy ], row, col ) + w11*getElevation( w1[gx+1][gy+1], row, col ); if ( missingData ) return Short.MIN_VALUE; double m = (1.-wlat) * m0 + wlat * m1; return (short)(m * 2); } private ReducedMedianFilter rmf = new ReducedMedianFilter( 256 ); private double getElevation( Weights w, int row, int col ) { if ( missingData ) { return 0.; } int nx = w.nx; int ny = w.ny; int mx = nx / 2; // mean pixels int my = ny / 2; // System.out.println( "nx="+ nx + " ny=" + ny ); rmf.reset(); for( int ix = 0; ix < nx; ix ++ ) { for( int iy = 0; iy < ny; iy ++ ) { short val = get( row + iy - my, col + ix - mx ); rmf.addSample( w.getWeight( ix, iy ), val ); } } return missingData ? 0. : rmf.calcEdgeReducedMedian( filterCenterFraction ); } private static class Weights { int nx; int ny; double[] weights; long total = 0; Weights( int nx, int ny ) { this.nx = nx; this.ny = ny; weights = new double[nx*ny]; } void inc( int ix, int iy ) { weights[ iy*nx + ix ] += 1.; total++; } void normalize( boolean verbose ) { for( int iy =0; iy < ny; iy++ ) { StringBuilder sb = verbose ? new StringBuilder() : null; for( int ix =0; ix < nx; ix++ ) { weights[ iy*nx + ix ] /= total; if ( sb != null ) { int iweight = (int)(1000*weights[ iy*nx + ix ] + 0.5 ); String sval = " " + iweight; sb.append( sval.substring( sval.length() - 4 ) ); } } if ( sb != null ) { System.out.println( sb ); System.out.println(); } } } double getWeight( int ix, int iy ) { return weights[ iy*nx + ix ]; } } private static int gridSteps = 10; private static Weights[][][] allShiftWeights = new Weights[17][][]; private static double filterCenterFraction = 0.2; private static double filterDiscRadius = 4.999; // in pixels static { String sRadius = System.getProperty( "filterDiscRadius" ); if ( sRadius != null && sRadius.length() > 0 ) { filterDiscRadius = Integer.parseInt( sRadius ); System.out.println( "using filterDiscRadius = " + filterDiscRadius ); } String sFraction = System.getProperty( "filterCenterFraction" ); if ( sFraction != null && sFraction.length() > 0 ) { filterCenterFraction = Integer.parseInt( sFraction ) / 100.; System.out.println( "using filterCenterFraction = " + filterCenterFraction ); } } // calculate interpolation weights from the overlap of a probe disc of given radius at given latitude // ( latIndex = 0 -> 0 deg, latIndex = 16 -> 80 degree) private static Weights[][] getWeights( int latIndex ) { int idx = latIndex < 16 ? latIndex : 16; Weights[][] res = allShiftWeights[idx]; if ( res == null ) { res = calcWeights( idx ); allShiftWeights[idx] = res; } return res; } private static Weights[][] calcWeights( int latIndex ) { double coslat = Math.cos( latIndex * 5. / 57.3 ); // radius in pixel units double ry = filterDiscRadius; double rx = ry / coslat; // gridsize is 2*radius + 1 cell int nx = ((int)rx) *2 + 3; int ny = ((int)ry) *2 + 3; System.out.println( "nx="+ nx + " ny=" + ny ); int mx = nx / 2; // mean pixels int my = ny / 2; // create a matrix for the relative intergrid-position Weights[][] shiftWeights = new Weights[gridSteps+1][]; // loop the intergrid-position for( int gx=0; gx<=gridSteps; gx++ ) { shiftWeights[gx] = new Weights[gridSteps+1]; double x0 = mx + ( (double)gx ) / gridSteps; for( int gy=0; gy<=gridSteps; gy++ ) { double y0 = my + ( (double)gy ) / gridSteps; // create the weight-matrix Weights weights = new Weights( nx, ny ); shiftWeights[gx][gy] = weights; double sampleStep = 0.001; for( double x = -1. + sampleStep/2.; x < 1.; x += sampleStep ) { double mx2 = 1. - x*x; int x_idx = (int)(x0 + x*rx); for( double y = -1. + sampleStep/2.; y < 1.; y += sampleStep ) { if ( y*y > mx2 ) { continue; } // we are in the ellipse, see what pixel we are on int y_idx = (int)(y0 + y*ry); weights.inc( x_idx, y_idx ); } } weights.normalize( true ); } } return shiftWeights; } }