/alaspatial/src/main/java/org/ala/spatial/util/GridCutter.java
Java | 717 lines | 510 code | 84 blank | 123 comment | 133 complexity | b69198513c8d42ded6a159cd5f32bfdd MD5 | raw file
1/** 2 * ************************************************************************ 3 * Copyright (C) 2010 Atlas of Living Australia All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with the 7 * License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ 8 * 9 * Software distributed under the License is distributed on an "AS IS" basis, 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for 11 * the specific language governing rights and limitations under the License. 12 * ************************************************************************* 13 */ 14package org.ala.spatial.util; 15 16import java.io.File; 17import java.io.FileWriter; 18import java.util.ArrayList; 19import java.util.TreeMap; 20import org.ala.layers.client.Client; 21import org.ala.layers.intersect.Grid; 22import org.ala.layers.intersect.SimpleRegion; 23import org.ala.layers.util.SpatialUtil; 24import org.ala.spatial.analysis.index.LayerFilter; 25 26/** 27 * Class for region cutting test data grids 28 * 29 * @author adam 30 */ 31public class GridCutter { 32 33 public static ArrayList<Object> loadCutGridsForAloc(File[] files, String extentsFilename, int pieces, AnalysisJob job) { 34 ArrayList<Object> data = new ArrayList<Object>(); 35 36 if (job != null) { 37 job.setProgress(0); 38 } 39 40 //determine outer bounds of layers 41 double xmin = Double.MAX_VALUE; 42 double ymin = Double.MAX_VALUE; 43 double xmax = Double.MAX_VALUE * -1; 44 double ymax = Double.MAX_VALUE * -1; 45 double xres = 0.01; 46 double yres = 0.01; 47 for (File f : files) { 48 String gridFilename = f.getPath().substring(0, f.getPath().length() - 4); 49 Grid g = new Grid(gridFilename); 50 xres = g.xres; 51 yres = g.xres; 52 if (xmin > g.xmin) { 53 xmin = g.xmin; 54 } 55 if (xmax < g.xmax) { 56 xmax = g.xmax; 57 } 58 if (ymin > g.ymin) { 59 ymin = g.ymin; 60 } 61 if (ymax < g.ymax) { 62 ymax = g.ymax; 63 } 64 } 65 66 if (files.length < 2) { 67 if (job != null) { 68 job.setCurrentState(AnalysisJob.FAILED); 69 job.log("Fewer than two layers with postive range."); 70 } else { 71 SpatialLogger.log("Fewer than two layers with postive range."); 72 } 73 return null; 74 } 75 76 77 //determine range and width's 78 double xrange = xmax - xmin; 79 double yrange = ymax - ymin; 80 int width = (int) Math.ceil(xrange / xres); 81 int height = (int) Math.ceil(yrange / yres); 82 83 //write extents into a file now 84 if (extentsFilename != null) { 85 try { 86 FileWriter fw = new FileWriter(extentsFilename); 87 fw.append(String.valueOf(width)).append("\n"); 88 fw.append(String.valueOf(height)).append("\n"); 89 fw.append(String.valueOf(xmin)).append("\n"); 90 fw.append(String.valueOf(ymin)).append("\n"); 91 fw.append(String.valueOf(xmax)).append("\n"); 92 fw.append(String.valueOf(ymax)); 93 fw.close(); 94 } catch (Exception e) { 95 e.printStackTrace(); 96 } 97 } 98 99 if (job != null) { 100 job.setProgress(0.1, "exported extents"); 101 } 102 103 //make cells list for outer bounds 104 int th = height; 105 int tw = width; 106 int tp = 0; 107 int[][] cells = new int[tw * th][2]; 108 for (int i = 0; i < height; i++) { 109 for (int j = 0; j < width; j++) { 110 cells[tp][0] = j; 111 cells[tp][1] = i; 112 tp++; 113 } 114 } 115 116 if (job != null) { 117 job.setProgress(0.2, "determined target cells"); 118 } 119 120 if (job != null) { 121 job.log("Cut cells count: " + cells.length); 122 } else { 123 System.out.println("Cut cells count: " + cells.length); 124 } 125 126 //transform cells numbers to long/lat numbers 127 double[][] points = new double[cells.length][2]; 128 for (int i = 0; i < cells.length; i++) { 129 points[i][0] = xmin + cells[i][0] * xres; 130 points[i][1] = ymin + cells[i][1] * yres; 131 } 132 133 //initialize data structure to hold everything 134 // each data piece: row1[col1, col2, ...] row2[col1, col2, ...] row3... 135 int remainingLength = cells.length; 136 int step = (int) Math.floor(remainingLength / (double) pieces); 137 for (int i = 0; i < pieces; i++) { 138 if (i == pieces - 1) { 139 data.add(new float[remainingLength * files.length]); 140 } else { 141 data.add(new float[step * files.length]); 142 remainingLength -= step; 143 } 144 } 145 146 //iterate for layers 147 double[] layerExtents = new double[files.length * 2]; 148 for (int j = 0; j < files.length; j++) { 149 String gridFilename = files[j].getPath().substring(0, files[j].getPath().length() - 4); 150 Grid g = new Grid(gridFilename); 151 float[] v = g.getValues2(points); 152 153 //row range standardization 154 float minv = Float.MAX_VALUE; 155 float maxv = Float.MAX_VALUE * -1; 156 for (int i = 0; i < v.length; i++) { 157 if (v[i] < minv) { 158 minv = v[i]; 159 } 160 if (v[i] > maxv) { 161 maxv = v[i]; 162 } 163 } 164 float range = maxv - minv; 165 if (range > 0) { 166 for (int i = 0; i < v.length; i++) { 167 v[i] = (v[i] - minv) / range; 168 } 169 } else { 170 for (int i = 0; i < v.length; i++) { 171 v[i] = 0; 172 } 173 } 174 layerExtents[j * 2] = minv; 175 layerExtents[j * 2 + 1] = maxv; 176 177 //iterate for pieces 178 for (int i = 0; i < pieces; i++) { 179 float[] d = (float[]) data.get(i); 180 for (int k = j, n = i * step; k < d.length; k += files.length, n++) { 181 d[k] = v[n]; 182 } 183 } 184 185 if (job != null) { 186 job.setProgress(0.2 + j / (double) files.length * 7 / 10.0, "opened grid: " + files[j].getName()); 187 } 188 } 189 190 if (job != null) { 191 job.log("finished opening grids"); 192 } 193 194 //remove null rows from data and cells 195 int newCellPos = 0; 196 int currentCellPos = 0; 197 for (int i = 0; i < pieces; i++) { 198 float[] d = (float[]) data.get(i); 199 int newPos = 0; 200 for (int k = 0; k < d.length; k += files.length) { 201 int nMissing = 0; 202 for (int j = 0; j < files.length; j++) { 203 if (Float.isNaN(d[k + j])) { 204 nMissing++; 205 } 206 } 207 //if (nMissing < files.length) { 208 if (nMissing == 0) { 209 if (newPos < k) { 210 for (int j = 0; j < files.length; j++) { 211 d[newPos + j] = d[k + j]; 212 } 213 } 214 newPos += files.length; 215 if (newCellPos < currentCellPos) { 216 cells[newCellPos][0] = cells[currentCellPos][0]; 217 cells[newCellPos][1] = cells[currentCellPos][1]; 218 } 219 newCellPos++; 220 } 221 currentCellPos++; 222 } 223 if (newPos < d.length) { 224 d = java.util.Arrays.copyOf(d, newPos); 225 data.set(i, d); 226 } 227 } 228 229 //remove zero length data pieces 230 for (int i = pieces - 1; i >= 0; i--) { 231 float[] d = (float[]) data.get(i); 232 if (d.length == 0) { 233 data.remove(i); 234 } 235 } 236 237 //add cells reference to output 238 data.add(cells); 239 240 //add extents to output 241 double[] extents = new double[6 + layerExtents.length]; 242 extents[0] = width; 243 extents[1] = height; 244 extents[2] = xmin; 245 extents[3] = ymin; 246 extents[4] = xmax; 247 extents[5] = ymax; 248 for (int i = 0; i < layerExtents.length; i++) { 249 extents[6 + i] = layerExtents[i]; 250 } 251 data.add(extents); 252 253 if (job != null) { 254 job.setProgress(1, "cleaned data"); 255 } 256 257 return data; 258 } 259 260 /** 261 * exports a list of layers cut against a region 262 * 263 * Cut layer files generated are input layers with grid cells outside of 264 * region set as missing. 265 * 266 * @param layers list of layer fieldIds to be cut as String[]. 267 * @param resolution target resolution as String 268 * @param region null or region to cut against as SimpleRegion. Cannot be 269 * used with envelopes. 270 * @param envelopes nul or region to cut against as LayerFilter[]. Cannot be 271 * used with region. 272 * @param extentsFilename output filename and path for writing output 273 * extents. 274 * @return directory containing the cut grid files. 275 */ 276 public static String cut2(String[] layers, String resolution, SimpleRegion region, LayerFilter[] envelopes, String extentsFilename) { 277 //check if resolution needs changing 278 resolution = confirmResolution(layers, resolution); 279 280 //get extents for all layers 281 double[][] extents = getLayerExtents(resolution, layers[0]); 282 for (int i = 1; i < layers.length; i++) { 283 extents = internalExtents(extents, getLayerExtents(resolution, layers[i])); 284 if (!isValidExtents(extents)) { 285 return null; 286 } 287 } 288 289 //get mask and adjust extents for filter 290 byte[][] mask; 291 int w = 0, h = 0; 292 double res = Double.parseDouble(resolution); 293 if (region != null) { 294 extents = internalExtents(extents, region.getBoundingBox()); 295 296 if (!isValidExtents(extents)) { 297 return null; 298 } 299 300 h = (int) Math.ceil((extents[1][1] - extents[0][1]) / res); 301 w = (int) Math.ceil((extents[1][0] - extents[0][0]) / res); 302 mask = getRegionMask(res, extents, w, h, region); 303 } else if (envelopes != null) { 304 h = (int) Math.ceil((extents[1][1] - extents[0][1]) / res); 305 w = (int) Math.ceil((extents[1][0] - extents[0][0]) / res); 306 mask = getEnvelopeMaskAndUpdateExtents(resolution, res, extents, h, w, envelopes); 307 h = (int) Math.ceil((extents[1][1] - extents[0][1]) / res); 308 w = (int) Math.ceil((extents[1][0] - extents[0][0]) / res); 309 } else { 310 h = (int) Math.ceil((extents[1][1] - extents[0][1]) / res); 311 w = (int) Math.ceil((extents[1][0] - extents[0][0]) / res); 312 mask = getMask(res, extents, w, h); 313 } 314 315 //mkdir in index location 316 String newPath = null; 317 try { 318 newPath = AlaspatialProperties.getAnalysisWorkingDir() + System.currentTimeMillis() + java.io.File.separator; 319 File directory = new File(newPath); 320 directory.mkdir(); 321 } catch (Exception e) { 322 e.printStackTrace(); 323 } 324 325 //apply mask 326 for (int i = 0; i < layers.length; i++) { 327 applyMask(newPath, resolution, extents, w, h, mask, layers[i]); 328 } 329 330 //write extents file 331 writeExtents(extentsFilename, extents, w, h); 332 333 return newPath; 334 } 335 336 static double[][] internalExtents(double[][] e1, double[][] e2) { 337 double[][] internalExtents = new double[2][2]; 338 339 internalExtents[0][0] = Math.max(e1[0][0], e2[0][0]); 340 internalExtents[0][1] = Math.max(e1[0][1], e2[0][1]); 341 internalExtents[1][0] = Math.min(e1[1][0], e2[1][0]); 342 internalExtents[1][1] = Math.min(e1[1][1], e2[1][1]); 343 344 return internalExtents; 345 } 346 347 static boolean isValidExtents(double[][] e) { 348 return e[0][0] < e[1][0] && e[0][1] < e[1][1]; 349 } 350 351 static double[][] getLayerExtents(String resolution, String layer) { 352 double[][] extents = new double[2][2]; 353 Grid g = Grid.getGrid(getLayerPath(resolution, layer)); 354 355 extents[0][0] = g.xmin; 356 extents[0][1] = g.ymin; 357 extents[1][0] = g.xmax; 358 extents[1][1] = g.ymax; 359 360 return extents; 361 } 362 363 public static String getLayerPath(String resolution, String layer) { 364 String field = Layers.getFieldId(layer); 365 366 File file = new File(AlaspatialProperties.getAnalysisLayersDir() + File.separator + resolution + File.separator + field + ".grd"); 367 368 //move up a resolution when the file does not exist at the target resolution 369 try { 370 while (!file.exists()) { 371 TreeMap<Double, String> resolutionDirs = new TreeMap<Double, String>(); 372 for (File dir : new File(AlaspatialProperties.getAnalysisLayersDir()).listFiles()) { 373 if (dir.isDirectory()) { 374 try { 375 resolutionDirs.put(Double.parseDouble(dir.getName()), dir.getName()); 376 } catch (Exception e) { 377 } 378 } 379 } 380 381 String newResolution = resolutionDirs.higherEntry(Double.parseDouble(resolution)).getValue(); 382 383 if (newResolution.equals(resolution)) { 384 break; 385 } else { 386 resolution = newResolution; 387 file = new File(AlaspatialProperties.getAnalysisLayersDir() + File.separator + resolution + File.separator + field + ".grd"); 388 } 389 } 390 } catch (Exception e) { 391 } 392 393 String layerPath = AlaspatialProperties.getAnalysisLayersDir() + File.separator + resolution + File.separator + field; 394 395 if (new File(layerPath + ".grd").exists()) { 396 return layerPath; 397 } else { 398 //look for an analysis layer 399 String[] info = Client.getLayerIntersectDao().getConfig().getAnalysisLayerInfo(layer); 400 if (info != null) { 401 return info[1]; 402 } else { 403 System.out.println("getLayerPath, cannot find for: " + layer + ", " + resolution); 404 return null; 405 } 406 } 407 } 408 409 static void applyMask(String dir, String resolution, double[][] extents, int w, int h, byte[][] mask, String layer) { 410 //layer output container 411 double[] dfiltered = new double[w * h]; 412 413 //open grid and get all data 414 Grid grid = Grid.getGrid(getLayerPath(resolution, layer)); 415 float[] d = grid.getGrid(); //get whole layer 416 417 //set all as missing values 418 for (int i = 0; i < dfiltered.length; i++) { 419 dfiltered[i] = Double.NaN; 420 } 421 422 double res = Double.parseDouble(resolution); 423 424 for (int i = 0; i < mask.length; i++) { 425 for (int j = 0; j < mask[0].length; j++) { 426 if (mask[i][j] > 0) { 427 dfiltered[j + (h - i - 1) * w] = grid.getValues2(new double[][]{{j * res + extents[0][0], i * res + extents[0][1]}})[0]; 428 } 429 } 430 } 431 432 grid.writeGrid(dir + layer, dfiltered, 433 extents[0][0], 434 extents[0][1], 435 extents[1][0], 436 extents[1][1], 437 res, res, h, w); 438 } 439 440 static void writeExtents(String filename, double[][] extents, int w, int h) { 441 if (filename != null) { 442 try { 443 FileWriter fw = new FileWriter(filename); 444 fw.append(String.valueOf(w)).append("\n"); 445 fw.append(String.valueOf(h)).append("\n"); 446 fw.append(String.valueOf(extents[0][0])).append("\n"); 447 fw.append(String.valueOf(extents[0][1])).append("\n"); 448 fw.append(String.valueOf(extents[1][0])).append("\n"); 449 fw.append(String.valueOf(extents[1][1])); 450 fw.close(); 451 } catch (Exception e) { 452 e.printStackTrace(); 453 } 454 } 455 } 456 457 /** 458 * Get a region mask. 459 * 460 * Note: using decimal degree grid, probably should be EPSG900913 grid. 461 * 462 * @param res resolution as double 463 * @param extents extents as double[][] with [0][0]=xmin, [0][1]=ymin, 464 * [1][0]=xmax, [1][1]=ymax. 465 * @param h height as int. 466 * @param w width as int. 467 * @param region area for the mask as SimpleRegion. 468 * @return 469 */ 470 private static byte[][] getRegionMask(double res, double[][] extents, int w, int h, SimpleRegion region) { 471 byte[][] mask = new byte[h][w]; 472 473 //can also use region.getOverlapGridCells_EPSG900913 474 region.getOverlapGridCells(extents[0][0], extents[0][1], extents[1][0], extents[1][1], w, h, mask); 475 for (int i = 0; i < h; i++) { 476 for (int j = 0; j < w; j++) { 477 //double tx = (j + 0.5) * res + extents[0][0]; 478 //double ty = (i + 0.5) * res + extents[0][1]; 479 //if (region.isWithin_EPSG900913(tx, ty)) { 480 // mask[i][j] = 1; 481 //} 482 if (mask[i][j] > 0) { 483 mask[i][j] = 1; 484 } 485 } 486 } 487 return mask; 488 } 489 490 private static byte[][] getMask(double res, double[][] extents, int w, int h) { 491 byte[][] mask = new byte[h][w]; 492 for (int i = 0; i < h; i++) { 493 for (int j = 0; j < w; j++) { 494 mask[i][j] = 1; 495 } 496 } 497 return mask; 498 } 499 500 /** 501 * Get a mask, 0=absence, 1=presence, for a given envelope and extents. 502 * 503 * @param resolution resolution as String. 504 * @param res resultions as double. 505 * @param extents extents as double[][] with [0][0]=xmin, [0][1]=ymin, 506 * [1][0]=xmax, [1][1]=ymax. 507 * @param h height as int. 508 * @param w width as int. 509 * @param envelopes 510 * @return mask as byte[][] 511 */ 512 private static byte[][] getEnvelopeMaskAndUpdateExtents(String resolution, double res, double[][] extents, int h, int w, LayerFilter[] envelopes) { 513 byte[][] mask = new byte[h][w]; 514 515 double[][] points = new double[h * w][2]; 516 for (int i = 0; i < w; i++) { 517 for (int j = 0; j < h; j++) { 518 points[i + j * w][0] = (double) (extents[0][0] + (i + 0.5) * res); 519 points[i + j * w][1] = (double) (extents[0][1] + (j + 0.5) * res); 520 //mask[j][i] = 0; 521 } 522 } 523 524 for (int k = 0; k < envelopes.length; k++) { 525 LayerFilter lf = envelopes[k]; 526 527 Grid grid = Grid.getGrid(getLayerPath(resolution, lf.getLayername())); 528 529 float[] d = grid.getValues3(points, 40960); 530 531 for (int i = 0; i < d.length; i++) { 532 if (lf.isValid(d[i])) { 533 mask[i / w][i % w]++; 534 } 535 } 536 } 537 538 for (int i = 0; i < w; i++) { 539 for (int j = 0; j < h; j++) { 540 if (mask[j][i] == envelopes.length) { 541 mask[j][i] = 1; 542 } else { 543 mask[j][i] = 0; 544 } 545 } 546 } 547 548 //find internal extents 549 int minx = w; 550 int maxx = -1; 551 int miny = h; 552 int maxy = -1; 553 for (int i = 0; i < w; i++) { 554 for (int j = 0; j < h; j++) { 555 if (mask[j][i] > 0) { 556 if (minx > i) { 557 minx = i; 558 } 559 if (maxx < i) { 560 maxx = i; 561 } 562 if (miny > j) { 563 miny = j; 564 } 565 if (maxy < j) { 566 maxy = j; 567 } 568 } 569 } 570 } 571 572 //reduce the size of the mask 573 int nw = maxx - minx + 1; 574 int nh = maxy - miny + 1; 575 byte[][] smallerMask = new byte[nh][nw]; 576 for (int i = minx; i < maxx; i++) { 577 for (int j = miny; j < maxy; j++) { 578 smallerMask[j - miny][i - minx] = mask[j][i]; 579 } 580 } 581 582 583 //update extents, must never be larger than the original extents (res is not negative, minx maxx miny mazy are not negative and < w & h respectively 584 extents[0][0] = Math.max(extents[0][0] + minx * res,extents[0][0]); //min x value 585 extents[1][0] = Math.min(extents[1][0] - (w - maxx - 1) * res,extents[1][0]); //max x value 586 extents[0][1] = Math.max(extents[0][1] + miny * res,extents[0][1]); //min y value 587 extents[1][1] = Math.min(extents[1][1] - (h - maxy - 1) * res,extents[1][1]); //max y value 588 589 return smallerMask; 590 } 591 592 /** 593 * Write a diva grid to disk for the envelope, 0 = absence, 1 = presence. 594 * 595 * @param filename output filename for the grid as String. 596 * @param resolution target resolution in decimal degrees as String. 597 * @param envelopes envelope specification as LayerFilter[]. 598 * @return area in sq km as double. 599 */ 600 public static double makeEnvelope(String filename, String resolution, LayerFilter[] envelopes) { 601 602 //get extents for all layers 603 double[][] extents = getLayerExtents(resolution, envelopes[0].getLayername()); 604 for (int i = 1; i < envelopes.length; i++) { 605 extents = internalExtents(extents, getLayerExtents(resolution, envelopes[i].getLayername())); 606 if (!isValidExtents(extents)) { 607 return -1; 608 } 609 } 610 611 double res = Double.parseDouble(resolution); 612 613 //limit the size of the grid files that can be generated 614 while ((Math.abs(extents[0][1] - extents[1][0])/res) * (Math.abs(extents[0][0] - extents[1][0])/res) > AlaspatialProperties.getAnalysisLimitGridCells()*2.0) { 615 res = res * 2; 616 } 617 if (res != Double.parseDouble(resolution)) { 618 resolution = String.format("%f", res); 619 } 620 621 //get mask and adjust extents for filter 622 byte[][] mask; 623 int w, h; 624 h = (int) Math.ceil((extents[1][1] - extents[0][1]) / res); 625 w = (int) Math.ceil((extents[1][0] - extents[0][0]) / res); 626 mask = getEnvelopeMaskAndUpdateExtents(resolution, res, extents, h, w, envelopes); 627 h = (int) Math.ceil((extents[1][1] - extents[0][1]) / res); 628 if(((int) Math.ceil((extents[1][1] + res - extents[0][1]) / res)) == h) { 629 extents[1][1] += res; 630 } 631 w = (int) Math.ceil((extents[1][0] - extents[0][0]) / res); 632 if(((int) Math.ceil((extents[1][0] + res - extents[0][0]) / res)) == w) { 633 extents[1][0] += res; 634 } 635 636 float[] values = new float[w * h]; 637 int pos = 0; 638 double areaSqKm = 0; 639 for (int i = h - 1; i >= 0; i--) { 640 for (int j = 0; j < w; j++) { 641 if(i < mask.length && j < mask[i].length) { 642 values[pos] = mask[i][j]; 643 644 if (mask[i][j] > 0) { 645 areaSqKm += SpatialUtil.cellArea(res, extents[0][1] + res * i); 646 } 647 } else { 648 values[pos] = 0; 649 } 650 pos++; 651 } 652 } 653 654 Grid grid = new Grid(getLayerPath(resolution, envelopes[0].getLayername())); 655 656 grid.writeGrid(filename, values, 657 extents[0][0], 658 extents[0][1], 659 extents[1][0], 660 extents[1][1], 661 res, res, h, w); 662 663 return areaSqKm; 664 } 665 666 667 /** 668 * Test if the layer filter is valid. 669 * 670 * The common problem is that a filter may refer to a layer that is not 671 * available. 672 * 673 * @param resolution target resolution as String. 674 * @param filter layer filter as LayerFilter[]. 675 * @return true iff valid filter. 676 */ 677 public static boolean isValidLayerFilter(String resolution, LayerFilter[] filter) { 678 for (LayerFilter lf : filter) { 679 if (GridCutter.getLayerPath(resolution, lf.getLayername()) == null) { 680 return false; 681 } 682 } 683 return true; 684 } 685 686 /** 687 * Determine the grid resolution that will be in use. 688 * 689 * @param layers list of layers to be used as String [] 690 * @param resolution target resolution as String 691 * @return resolution that will be used 692 */ 693 private static String confirmResolution(String[] layers, String resolution) { 694 try { 695 TreeMap<Double, String> resolutions = new TreeMap<Double, String>(); 696 for (String layer : layers) { 697 String path = GridCutter.getLayerPath(resolution, layer); 698 int end, start; 699 if (path != null 700 && ((end = path.lastIndexOf(File.separator)) > 0) 701 && ((start = path.lastIndexOf(File.separator, end - 1)) > 0)) { 702 String res = path.substring(start + 1, end); 703 Double d = Double.parseDouble(res); 704 if (d < 1) { 705 resolutions.put(d, res); 706 } 707 } 708 } 709 if (resolutions.size() > 0) { 710 resolution = resolutions.firstEntry().getValue(); 711 } 712 } catch (Exception e) { 713 e.printStackTrace(); 714 } 715 return resolution; 716 } 717}