diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 30aa626c2..000000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/worldwind/src/main/java/gov/nasa/worldwind/WorldWind.java b/worldwind/src/main/java/gov/nasa/worldwind/WorldWind.java index 1af68cc1b..1e84ec8e9 100644 --- a/worldwind/src/main/java/gov/nasa/worldwind/WorldWind.java +++ b/worldwind/src/main/java/gov/nasa/worldwind/WorldWind.java @@ -16,6 +16,9 @@ public class WorldWind { + public static final String MAP_CACHE_PATH = android.os.Environment.getExternalStorageDirectory().getAbsolutePath() + "/worldwind/map/"; + public static final String ELE_CACHE_PATH = android.os.Environment.getExternalStorageDirectory().getAbsolutePath() + "/worldwind/ele/"; + /** * {@link AltitudeMode} constant indicating an altitude relative to the globe's ellipsoid. Ignores the elevation of * the terrain directly beneath the position's latitude and longitude. diff --git a/worldwind/src/main/java/gov/nasa/worldwind/geom/Angle.java b/worldwind/src/main/java/gov/nasa/worldwind/geom/Angle.java new file mode 100644 index 000000000..afcf2effb --- /dev/null +++ b/worldwind/src/main/java/gov/nasa/worldwind/geom/Angle.java @@ -0,0 +1,433 @@ +package gov.nasa.worldwind.geom; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Angle { + public static final String ANGLE_FORMAT_DD = "gov.nasa.worldwind.Geom.AngleDD"; + public static final String ANGLE_FORMAT_DM = "gov.nasa.worldwind.Geom.AngleDM"; + public static final String ANGLE_FORMAT_DMS = "gov.nasa.worldwind.Geom.AngleDMS"; + public static final Angle ZERO = fromDegrees(0.0D); + public static final Angle POS90 = fromDegrees(90.0D); + public static final Angle NEG90 = fromDegrees(-90.0D); + public static final Angle POS180 = fromDegrees(180.0D); + public static final Angle NEG180 = fromDegrees(-180.0D); + public static final Angle POS360 = fromDegrees(360.0D); + public static final Angle NEG360 = fromDegrees(-360.0D); + public static final Angle MINUTE = fromDegrees(0.016666666666666666D); + public static final Angle SECOND = fromDegrees(2.777777777777778E-4D); + private static final double DEGREES_TO_RADIANS = 0.017453292519943295D; + private static final double RADIANS_TO_DEGREES = 57.29577951308232D; + private static final double PIOver2 = 1.5707963267948966D; + public final double degrees; + public final double radians; + + public static Angle fromDegrees(double degrees) { + return new Angle(degrees, 0.017453292519943295D * degrees); + } + + public static Angle fromRadians(double radians) { + return new Angle(57.29577951308232D * radians, radians); + } + + public static Angle fromDegreesLatitude(double degrees) { + degrees = degrees < -90.0D ? -90.0D : (degrees > 90.0D ? 90.0D : degrees); + double radians = 0.017453292519943295D * degrees; + radians = radians < -1.5707963267948966D ? -1.5707963267948966D : (radians > 1.5707963267948966D ? 1.5707963267948966D : radians); + return new Angle(degrees, radians); + } + + public static Angle fromRadiansLatitude(double radians) { + radians = radians < -1.5707963267948966D ? -1.5707963267948966D : (radians > 1.5707963267948966D ? 1.5707963267948966D : radians); + double degrees = 57.29577951308232D * radians; + degrees = degrees < -90.0D ? -90.0D : (degrees > 90.0D ? 90.0D : degrees); + return new Angle(degrees, radians); + } + + public static Angle fromDegreesLongitude(double degrees) { + degrees = degrees < -180.0D ? -180.0D : (degrees > 180.0D ? 180.0D : degrees); + double radians = 0.017453292519943295D * degrees; + radians = radians < -3.141592653589793D ? -3.141592653589793D : (radians > 3.141592653589793D ? 3.141592653589793D : radians); + return new Angle(degrees, radians); + } + + public static Angle fromRadiansLongitude(double radians) { + radians = radians < -3.141592653589793D ? -3.141592653589793D : (radians > 3.141592653589793D ? 3.141592653589793D : radians); + double degrees = 57.29577951308232D * radians; + degrees = degrees < -180.0D ? -180.0D : (degrees > 180.0D ? 180.0D : degrees); + return new Angle(degrees, radians); + } + + public static Angle fromXY(double x, double y) { + double radians = Math.atan2(y, x); + return new Angle(57.29577951308232D * radians, radians); + } + + public static Angle fromDMS(int degrees, int minutes, int seconds) { + String message; + if (degrees < 0) { + } else if (minutes >= 0 && minutes < 60) { + if (seconds >= 0 && seconds < 60) { + return fromDegrees((double)degrees + (double)minutes / 60.0D + (double)seconds / 3600.0D); + } else { + } + } else { + } + return null; + } + + public static Angle fromDMdS(int degrees, double minutes) { + return fromDegrees((double)degrees + minutes / 60.0D); + } + + public static Angle fromDMS(String dmsString) { + String regex; + regex = "([-|\\+]?\\d{1,3}[d|D|°|\\s](\\s*\\d{1,2}['|’|\\s])?(\\s*\\d{1,2}[\"|”|\\s])?\\s*([N|n|S|s|E|e|W|w])?\\s?)"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(dmsString + " "); + dmsString = dmsString.replaceAll("[D|d|°|'|’|\"|”]", " "); + dmsString = dmsString.replaceAll("\\s+", " "); + dmsString = dmsString.trim(); + int sign = 1; + char suffix = dmsString.toUpperCase().charAt(dmsString.length() - 1); + char prefix = dmsString.charAt(0); + if (!Character.isDigit(suffix)) { + sign = suffix != 'S' && suffix != 'W' ? 1 : -1; + dmsString = dmsString.substring(0, dmsString.length() - 1); + dmsString = dmsString.trim(); + if (!Character.isDigit(prefix)) { + dmsString = dmsString.substring(1, dmsString.length()); + dmsString = dmsString.trim(); + } + } else if (!Character.isDigit(prefix)) { + sign *= prefix == '-' ? -1 : 1; + dmsString = dmsString.substring(1, dmsString.length()); + } + + String[] DMS = dmsString.split(" "); + int d = Integer.parseInt(DMS[0]); + int m = DMS.length > 1 ? Integer.parseInt(DMS[1]) : 0; + int s = DMS.length > 2 ? Integer.parseInt(DMS[2]) : 0; + return fromDMS(d, m, s).multiply((double)sign); + } + + public Angle(Angle angle) { + this.degrees = angle.degrees; + this.radians = angle.radians; + } + + private Angle(double degrees, double radians) { + this.degrees = degrees; + this.radians = radians; + } + + public final double getDegrees() { + return this.degrees; + } + + public final double getRadians() { + return this.radians; + } + + public final Angle add(Angle angle) { + return fromDegrees(this.degrees + angle.degrees); + } + + public final Angle subtract(Angle angle) { + return fromDegrees(this.degrees - angle.degrees); + } + + public final Angle multiply(double multiplier) { + return fromDegrees(this.degrees * multiplier); + } + + public final double divide(Angle angle) { + return this.degrees / angle.degrees; + } + + public final Angle addDegrees(double degrees) { + return fromDegrees(this.degrees + degrees); + } + + public final Angle subtractDegrees(double degrees) { + return fromDegrees(this.degrees - degrees); + } + + public final Angle divide(double divisor) { + return fromDegrees(this.degrees / divisor); + } + + public final Angle addRadians(double radians) { + return fromRadians(this.radians + radians); + } + + public final Angle subtractRadians(double radians) { + return fromRadians(this.radians - radians); + } + + public Angle angularDistanceTo(Angle angle) { + double differenceDegrees = angle.subtract(this).degrees; + if (differenceDegrees < -180.0D) { + differenceDegrees += 360.0D; + } else if (differenceDegrees > 180.0D) { + differenceDegrees -= 360.0D; + } + + double absAngle = Math.abs(differenceDegrees); + return fromDegrees(absAngle); + } + + public final double sin() { + return Math.sin(this.radians); + } + + public final double sinHalfAngle() { + return Math.sin(0.5D * this.radians); + } + + public static Angle asin(double sine) { + return fromRadians(Math.asin(sine)); + } + + public static double arctanh(double radians) { + return 0.5D * Math.log((1.0D + radians) / (1.0D - radians)); + } + + public final double cos() { + return Math.cos(this.radians); + } + + public final double cosHalfAngle() { + return Math.cos(0.5D * this.radians); + } + + public static Angle acos(double cosine) { + return fromRadians(Math.acos(cosine)); + } + + public final double tanHalfAngle() { + return Math.tan(0.5D * this.radians); + } + + public static Angle atan(double tan) { + return fromRadians(Math.atan(tan)); + } + + public static Angle midAngle(Angle a1, Angle a2) { + return fromDegrees(0.5D * (a1.degrees + a2.degrees)); + } + + public static Angle average(Angle a, Angle b) { + return fromDegrees(0.5D * (a.degrees + b.degrees)); + } + + public static Angle average(Angle a, Angle b, Angle c) { + return fromDegrees((a.degrees + b.degrees + c.degrees) / 3.0D); + } + + public static Angle clamp(Angle value, Angle min, Angle max) { + return value.degrees < min.degrees ? min : (value.degrees > max.degrees ? max : value); + } + +// public static Angle mix(double amount, Angle value1, Angle value2) { +// if (value1 != null && value2 != null) { +// if (amount < 0.0D) { +// return value1; +// } else if (amount > 1.0D) { +// return value2; +// } else { +// Quaternion quat = Quaternion.slerp(amount, Quaternion.fromAxisAngle(value1, Vec4.UNIT_X), Quaternion.fromAxisAngle(value2, Vec4.UNIT_X)); +// Angle angle = quat.getRotationX(); +// return Double.isNaN(angle.degrees) ? null : angle; +// } +// } +// } + +// public final int compareTo(Angle angle) { +// if (angle == null) { +// String msg = Logging.getMessage("nullValue.AngleIsNull"); +// Logging.logger().severe(msg); +// throw new IllegalArgumentException(msg); +// } else if (this.degrees < angle.degrees) { +// return -1; +// } else { +// return this.degrees > angle.degrees ? 1 : 0; +// } +// } + + public static double normalizedDegrees(double degrees) { + double a = degrees % 360.0D; + return a > 180.0D ? a - 360.0D : (a < -180.0D ? 360.0D + a : a); + } + + public static double normalizedDegreesLatitude(double degrees) { + double lat = degrees % 180.0D; + return lat > 90.0D ? 180.0D - lat : (lat < -90.0D ? -180.0D - lat : lat); + } + + public static double normalizedDegreesLongitude(double degrees) { + double lon = degrees % 360.0D; + return lon > 180.0D ? lon - 360.0D : (lon < -180.0D ? 360.0D + lon : lon); + } + + public static Angle normalizedAngle(Angle unnormalizedAngle) { + return fromDegrees(normalizedDegrees(unnormalizedAngle.degrees)); + } + + public static Angle normalizedLatitude(Angle unnormalizedAngle) { + return fromDegrees(normalizedDegreesLatitude(unnormalizedAngle.degrees)); + } + + public static Angle normalizedLongitude(Angle unnormalizedAngle) { + return fromDegrees(normalizedDegreesLongitude(unnormalizedAngle.degrees)); + } + + public Angle normalize() { + return normalizedAngle(this); + } + + public Angle normalizedLatitude() { + return normalizedLatitude(this); + } + + public Angle normalizedLongitude() { + return normalizedLongitude(this); + } + + public static boolean crossesLongitudeBoundary(Angle angleA, Angle angleB) { + return Math.signum(angleA.degrees) != Math.signum(angleB.degrees) && Math.abs(angleA.degrees - angleB.degrees) > 180.0D; + } + + public static boolean isValidLatitude(double value) { + return value >= -90.0D && value <= 90.0D; + } + + public static boolean isValidLongitude(double value) { + return value >= -180.0D && value <= 180.0D; + } + + public static Angle max(Angle a, Angle b) { + return a.degrees >= b.degrees ? a : b; + } + + public static Angle min(Angle a, Angle b) { + return a.degrees <= b.degrees ? a : b; + } + + public final String toString() { + return Double.toString(this.degrees) + '°'; + } + + public final String toDecimalDegreesString(int digits) { + return String.format("%." + digits + "f°", this.degrees); + } + + public final String toDMSString() { + double temp = this.degrees; + int sign = (int)Math.signum(temp); + temp *= (double)sign; + int d = (int)Math.floor(temp); + temp = (temp - (double)d) * 60.0D; + int m = (int)Math.floor(temp); + temp = (temp - (double)m) * 60.0D; + int s = (int)Math.round(temp); + if (s == 60) { + ++m; + s = 0; + } + + if (m == 60) { + ++d; + m = 0; + } + + return (sign == -1 ? "-" : "") + d + '°' + ' ' + m + '’' + ' ' + s + '”'; + } + + public final String toDMString() { + double temp = this.degrees; + int sign = (int)Math.signum(temp); + temp *= (double)sign; + int d = (int)Math.floor(temp); + temp = (temp - (double)d) * 60.0D; + int m = (int)Math.floor(temp); + temp = (temp - (double)m) * 60.0D; + int s = (int)Math.round(temp); + if (s == 60) { + ++m; + s = 0; + } + + if (m == 60) { + ++d; + m = 0; + } + + double mf = s == 0 ? (double)m : (double)m + (double)s / 60.0D; + return (sign == -1 ? "-" : "") + d + '°' + ' ' + String.format("%5.2f", mf) + '’'; + } + + public final String toFormattedDMSString() { + double temp = this.degrees; + int sign = (int)Math.signum(temp); + temp *= (double)sign; + int d = (int)Math.floor(temp); + temp = (temp - (double)d) * 60.0D; + int m = (int)Math.floor(temp); + temp = (temp - (double)m) * 60.0D; + double s = Math.rint(temp * 100.0D) / 100.0D; + if (s == 60.0D) { + ++m; + s = 0.0D; + } + + if (m == 60) { + ++d; + m = 0; + } + + return String.format("%4d° %2d’ %5.2f”", sign * d, m, s); + } + + public final double[] toDMS() { + double temp = this.degrees; + int sign = (int)Math.signum(temp); + temp *= (double)sign; + int d = (int)Math.floor(temp); + temp = (temp - (double)d) * 60.0D; + int m = (int)Math.floor(temp); + temp = (temp - (double)m) * 60.0D; + double s = Math.rint(temp * 100.0D) / 100.0D; + if (s == 60.0D) { + ++m; + s = 0.0D; + } + + if (m == 60) { + ++d; + m = 0; + } + + return new double[]{(double)(sign * d), (double)m, s}; + } + + public long getSizeInBytes() { + return 8L; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (o != null && this.getClass() == o.getClass()) { + Angle angle = (Angle)o; + return angle.degrees == this.degrees; + } else { + return false; + } + } + + public int hashCode() { + long temp = this.degrees != 0.0D ? Double.doubleToLongBits(this.degrees) : 0L; + return (int)(temp ^ temp >>> 32); + } +} diff --git a/worldwind/src/main/java/gov/nasa/worldwind/geom/Sector.java b/worldwind/src/main/java/gov/nasa/worldwind/geom/Sector.java index cafc5578d..bae516564 100644 --- a/worldwind/src/main/java/gov/nasa/worldwind/geom/Sector.java +++ b/worldwind/src/main/java/gov/nasa/worldwind/geom/Sector.java @@ -32,6 +32,8 @@ public class Sector { */ protected double maxLongitude = Double.NaN; + protected double deltaLat = Double.NaN, deltaLon = Double.NaN; + /** * Constructs an empty sector with minimum and maximum latitudes and longitudes all NaN. */ @@ -53,6 +55,17 @@ public Sector(double minLatitude, double minLongitude, double deltaLatitude, dou this.maxLongitude = Location.clampLongitude(minLongitude + (deltaLongitude > 0 ? deltaLongitude : Double.NaN)); } + public Sector(Angle minLatitude, Angle maxLatitude, Angle minLongitude, Angle maxLongitude) { + if (minLatitude != null && maxLatitude != null && minLongitude != null && maxLongitude != null) { + this.minLatitude = minLatitude.degrees; + this.maxLatitude = maxLatitude.degrees; + this.minLongitude = minLongitude.degrees; + this.maxLongitude = maxLongitude.degrees; + this.deltaLat = Angle.fromDegrees(this.maxLatitude - this.minLatitude).degrees; + this.deltaLon = Angle.fromDegrees(this.maxLongitude - this.minLongitude).degrees; + } + } + /** * Constructs a sector with the minimum and maximum latitudes and longitudes of a specified sector. * @@ -71,6 +84,10 @@ public Sector(Sector sector) { this.maxLongitude = sector.maxLongitude; } + public static Sector fromDegreesMaxMin(double minLatitude, double maxLatitude, double minLongitude, double maxLongitude) { + return new Sector(Angle.fromDegrees(minLatitude), Angle.fromDegrees(maxLatitude), Angle.fromDegrees(minLongitude), Angle.fromDegrees(maxLongitude)); + } + public static Sector fromDegrees(double latitudeDegrees, double longitudeDegrees, double deltaLatitudeDegrees, double deltaLongitudeDegrees) { return new Sector( diff --git a/worldwind/src/main/java/gov/nasa/worldwind/globe/ElevationRetriever.java b/worldwind/src/main/java/gov/nasa/worldwind/globe/ElevationRetriever.java index b2696448b..2b7a12a8a 100644 --- a/worldwind/src/main/java/gov/nasa/worldwind/globe/ElevationRetriever.java +++ b/worldwind/src/main/java/gov/nasa/worldwind/globe/ElevationRetriever.java @@ -6,6 +6,9 @@ package gov.nasa.worldwind.globe; import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -14,6 +17,7 @@ import java.nio.ByteOrder; import java.nio.ShortBuffer; +import gov.nasa.worldwind.WorldWind; import gov.nasa.worldwind.formats.tiff.Subfile; import gov.nasa.worldwind.formats.tiff.Tiff; import gov.nasa.worldwind.render.ImageSource; @@ -37,7 +41,7 @@ public ElevationRetriever(int maxSimultaneousRetrievals) { } @Override - protected void retrieveAsync(ImageSource key, Void unused, Callback callback) { + protected void retrieveAsync(ImageSource key, Void unused, Callback callback) { //TODO : OFFLINE MAPPING try { ShortBuffer buffer = this.decodeCoverage(key); @@ -53,7 +57,12 @@ protected void retrieveAsync(ImageSource key, Void unused, Callback callback) { + Callback callback) { //TODO : OFFLINE MAPPING try { Bitmap bitmap = this.decodeImage(imageSource, imageOptions); @@ -72,12 +75,19 @@ protected Bitmap decodeImage(ImageSource imageSource, ImageOptions imageOptions) } if (imageSource.isUrl()) { - return this.decodeUrl(imageSource.asUrl(), imageOptions); + File localCache = WWUtil.checkLocalCache(false, imageSource.asUrl(), WorldWind.MAP_CACHE_PATH); + if(localCache == null) + return this.decodeUrl(imageSource.asUrl(), imageOptions); + else { + return this.decodeFilePath(localCache.getAbsolutePath(), imageOptions); + } } return this.decodeUnrecognized(imageSource); } + + protected Bitmap decodeResource(int id, ImageOptions imageOptions) { BitmapFactory.Options factoryOptions = this.bitmapFactoryOptions(imageOptions); return (this.resources != null) ? BitmapFactory.decodeResource(this.resources, id, factoryOptions) : null; @@ -102,12 +112,35 @@ protected Bitmap decodeUrl(String urlString, ImageOptions imageOptions) throws I stream = new BufferedInputStream(conn.getInputStream()); BitmapFactory.Options factoryOptions = this.bitmapFactoryOptions(imageOptions); - return BitmapFactory.decodeStream(stream, null, factoryOptions); + Bitmap bitmap = BitmapFactory.decodeStream(stream, null, factoryOptions); + encodeBitmapToFile(bitmap, urlString); + return bitmap; } finally { WWUtil.closeSilently(stream); } } + protected void encodeBitmapToFile(Bitmap bitmap, String url) { + try { + File f = new File(WorldWind.MAP_CACHE_PATH + WWUtil.resolveBBOX(url)); + + if(f.createNewFile()) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos); + byte[] bitmapdata = bos.toByteArray(); + + FileOutputStream fos = new FileOutputStream(f); + fos.write(bitmapdata); + fos.flush(); + fos.close(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + + protected Bitmap decodeUnrecognized(ImageSource imageSource) { Logger.log(Logger.WARN, "Unrecognized image source \'" + imageSource + "\'"); return null; diff --git a/worldwind/src/main/java/gov/nasa/worldwind/util/WWUtil.java b/worldwind/src/main/java/gov/nasa/worldwind/util/WWUtil.java index 72b6f2c7c..e35992f89 100644 --- a/worldwind/src/main/java/gov/nasa/worldwind/util/WWUtil.java +++ b/worldwind/src/main/java/gov/nasa/worldwind/util/WWUtil.java @@ -10,12 +10,15 @@ import java.io.BufferedReader; import java.io.Closeable; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; +import gov.nasa.worldwind.WorldWind; + public class WWUtil { protected static final char LINE_SEPARATOR = '\n'; @@ -83,4 +86,44 @@ public static String readResourceAsText(Resources resources, @RawRes int id) thr closeSilently(reader); } } + + + /** + * Checks Local Cache + * */ + public static File checkLocalCache(boolean isForceOnline, String url, String parentDir) { + if(!isForceOnline) { + // ExUrl: https://worldwind26.arc.nasa.gov/elev?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&LAYERS=GEBCO,aster_v2,USGS-NED&STYLES=&CRS=EPSG:4326&BBOX=0.0,-90.0,90.0,0.0&WIDTH=256&HEIGHT=256&FORMAT=application/bil16&TRANSPARENT=TRUE + // Important part 4 values of BBOX + String eq = resolveBBOX(url); + File dir = new File(parentDir); + if(!dir.exists()) + dir.mkdirs(); + if(dir.canRead() && dir.canWrite()) { + for (File file : dir.listFiles()) { + if (file.isFile()) { + if(file.getName().equals(eq)) { + return file; + } + } + } + } + } + return null; + } + + public static String getFormat(String url) { + //https://worldwind26.arc.nasa.gov/elev?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&LAYERS=GEBCO,aster_v2,USGS-NED&STYLES=&CRS=EPSG:4326&BBOX=-90.0,-180.0,0.0,-90.0&WIDTH=256&HEIGHT=256&FORMAT=application/bil16&TRANSPARENT=TRUE + //&FORMAT=application/bil16& + //bil16 + String[] formatSplt = url.split("FORMAT="); // 2 Parts + String formatVal = formatSplt[1].split("&")[0].split("/")[1]; + return formatVal.toLowerCase(); + } + + public static String resolveBBOX(String url) { + String[] bboxSplit = url.split("BBOX="); // 2 Parts + String bboxValues = bboxSplit[1].split("&")[0]; + return bboxValues.replaceAll(",","a").replace(".","b").replace("-","c"); + } }