protected void makeTessellatedLocations( Globe globe, int subdivisions, List<LatLon> locations, List<LatLon> tessellatedLocations) { ArrayList<Vec4> points = new ArrayList<Vec4>(); for (LatLon ll : locations) { points.add(globe.computePointFromLocation(ll)); } //noinspection StringEquality if (WWMath.computeWindingOrderOfLocations(locations) != AVKey.COUNTER_CLOCKWISE) Collections.reverse(locations); Vec4 centerPoint = Vec4.computeAveragePoint(points); Vec4 surfaceNormal = globe.computeSurfaceNormalAtPoint(centerPoint); int numPoints = points.size(); float[] coords = new float[3 * numPoints]; for (int i = 0; i < numPoints; i++) { points.get(i).toFloatArray(coords, 3 * i, 3); } GeometryBuilder gb = new GeometryBuilder(); GeometryBuilder.IndexedTriangleArray tessellatedPoints = gb.tessellatePolygon(0, numPoints, coords, surfaceNormal); for (int i = 0; i < subdivisions; i++) { gb.subdivideIndexedTriangleArray(tessellatedPoints); } for (int i = 0; i < tessellatedPoints.getVertexCount(); i++) { Vec4 v = Vec4.fromFloatArray(tessellatedPoints.getVertices(), 3 * i, 3); tessellatedLocations.add(globe.computePositionFromPoint(v)); } }
/** * Computes a <code>Box</code> that bounds a specified buffer of points. Principal axes are * computed for the points and used to form a <code>Box</code>. This returns <code>null</code> if * the buffer is empty or contains only a partial point. * * <p>The buffer must contain XYZ coordinate tuples which are either tightly packed or offset by * the specified stride. The stride specifies the number of buffer elements between the first * coordinate of consecutive tuples. For example, a stride of 3 specifies that each tuple is * tightly packed as XYZXYZXYZ, whereas a stride of 5 specifies that there are two elements * between each tuple as XYZabXYZab (the elements "a" and "b" are ignored). The stride must be at * least 3. If the buffer's length is not evenly divisible into stride-sized tuples, this ignores * the remaining elements that follow the last complete tuple. * * @param buffer the buffer containing the point coordinates for which to compute a bounding * volume. * @param stride the number of elements between the first coordinate of consecutive points. If * stride is 3, this interprets the buffer has having tightly packed XYZ coordinate tuples. * @return the bounding volume, with axes lengths consistent with the conventions described in the * <code>Box</code> class overview. * @throws IllegalArgumentException if the buffer is null or empty, or if the stride is less than * three. */ public static Box computeBoundingBox(FloatBuffer buffer, int stride) { if (buffer == null) { String msg = Logging.getMessage("nullValue.BufferIsNull"); Logging.error(msg); throw new IllegalArgumentException(msg); } if (stride < 3) { String msg = Logging.getMessage("generic.StrideIsInvalid", stride); Logging.error(msg); throw new IllegalArgumentException(msg); } Vec4[] axes = WWMath.computePrincipalAxes(buffer, stride); if (axes == null) { String msg = Logging.getMessage("generic.BufferIsEmpty"); Logging.error(msg); throw new IllegalArgumentException(msg); } Vec4 r = axes[0]; Vec4 s = axes[1]; Vec4 t = axes[2]; // Find the extremes along each axis. double minDotR = Double.MAX_VALUE; double maxDotR = -minDotR; double minDotS = Double.MAX_VALUE; double maxDotS = -minDotS; double minDotT = Double.MAX_VALUE; double maxDotT = -minDotT; for (int i = buffer.position(); i <= buffer.limit() - stride; i += stride) { double x = buffer.get(i); double y = buffer.get(i + 1); double z = buffer.get(i + 2); double pdr = x * r.x + y * r.y + z * r.z; if (pdr < minDotR) minDotR = pdr; if (pdr > maxDotR) maxDotR = pdr; double pds = x * s.x + y * s.y + z * s.z; if (pds < minDotS) minDotS = pds; if (pds > maxDotS) maxDotS = pds; double pdt = x * t.x + y * t.y + z * t.z; if (pdt < minDotT) minDotT = pdt; if (pdt > maxDotT) maxDotT = pdt; } if (maxDotR == minDotR) maxDotR = minDotR + 1; if (maxDotS == minDotS) maxDotS = minDotS + 1; if (maxDotT == minDotT) maxDotT = minDotT + 1; return new Box(axes, minDotR, maxDotR, minDotS, maxDotS, minDotT, maxDotT); }
/** * Compute a <code>Box</code> that bounds a specified list of points. Principal axes are computed * for the points and used to form a <code>Box</code>. * * @param points the points for which to compute a bounding volume. * @return the bounding volume, with axes lengths consistent with the conventions described in the * overview. * @throws IllegalArgumentException if the point list is null or empty. */ public static Box computeBoundingBox(Iterable<? extends Vec4> points) { if (points == null) { String msg = Logging.getMessage("nullValue.PointListIsNull"); Logging.error(msg); throw new IllegalArgumentException(msg); } Vec4[] axes = WWMath.computePrincipalAxes(points); if (axes == null) { String msg = Logging.getMessage("generic.PointListIsEmpty"); Logging.error(msg); throw new IllegalArgumentException(msg); } Vec4 r = axes[0]; Vec4 s = axes[1]; Vec4 t = axes[2]; // Find the extremes along each axis. double minDotR = Double.MAX_VALUE; double maxDotR = -minDotR; double minDotS = Double.MAX_VALUE; double maxDotS = -minDotS; double minDotT = Double.MAX_VALUE; double maxDotT = -minDotT; for (Vec4 p : points) { if (p == null) continue; double pdr = p.dot3(r); if (pdr < minDotR) minDotR = pdr; if (pdr > maxDotR) maxDotR = pdr; double pds = p.dot3(s); if (pds < minDotS) minDotS = pds; if (pds > maxDotS) maxDotS = pds; double pdt = p.dot3(t); if (pdt < minDotT) minDotT = pdt; if (pdt > maxDotT) maxDotT = pdt; } if (maxDotR == minDotR) maxDotR = minDotR + 1; if (maxDotS == minDotS) maxDotS = minDotS + 1; if (maxDotT == minDotT) maxDotT = minDotT + 1; return new Box(axes, minDotR, maxDotR, minDotS, maxDotS, minDotT, maxDotT); }
/** * Rectify elevation raster. For best performance each elevation raster must have correct * parameters and values set. The <code>rectify()</code> operation validates that correct * Elevation min and max values are set or calculated. All values that beyond min/max and voids, * must be marked with "Missing Signal" (aka "nodata" value). * * @param raster A DataRaster to rectify * @throws IllegalArgumentException if <code>raster</code> is <code>null</code> */ public static void rectify(ByteBufferRaster raster) throws IllegalArgumentException { if (null == raster) { String msg = Logging.getMessage("nullValue.RasterIsNull"); Logging.logger().finest(msg); throw new IllegalArgumentException(msg); } int width = raster.getWidth(); int height = raster.getHeight(); if (width == 0 || height == 0) { // nothing to do return; } double[] minmax = raster.getExtremes(); if (null == minmax) { // nothing to do return; } Double minValue = minmax[0]; Double maxValue = minmax[1]; Double missingDataSignal = AVListImpl.getDoubleValue(raster, AVKey.MISSING_DATA_SIGNAL, null); // check if the minimum value is one of the well known NODATA values if (ElevationsUtil.isKnownMissingSignal(minValue) || (missingDataSignal != null && missingDataSignal.equals(minValue))) { missingDataSignal = minValue; raster.setTransparentValue(missingDataSignal); minmax = raster.getExtremes(); if (null != minmax) { minValue = minmax[0]; maxValue = minmax[1]; } } BufferWrapper bufferWrapper = raster.getBuffer(); // Allocate a buffer to hold one row of scalar values. double[] array = new double[width]; boolean needsConversion = false; double conversionValue = 1d; if (raster.hasKey(AVKey.ELEVATION_UNIT)) { String unit = raster.getStringValue(AVKey.ELEVATION_UNIT); if (AVKey.UNIT_METER.equalsIgnoreCase(unit)) { needsConversion = false; } else if (AVKey.UNIT_FOOT.equalsIgnoreCase(unit)) { needsConversion = true; conversionValue = WWMath.convertFeetToMeters(1); minValue = WWMath.convertFeetToMeters(minValue); maxValue = WWMath.convertFeetToMeters(maxValue); raster.setValue(AVKey.ELEVATION_UNIT, AVKey.UNIT_METER); } else { needsConversion = false; String msg = Logging.getMessage("generic.UnrecognizedElevationUnit", unit); Logging.logger().warning(msg); } } boolean rasterHasVoids = false; for (int j = 0; j < height; j++) { bufferWrapper.getDouble(j * width, array, 0, width); boolean commitChanges = false; for (int i = 0; i < width; i++) { double value = array[i]; if (null != missingDataSignal && value == missingDataSignal) { rasterHasVoids = true; } else { if (needsConversion) { value *= conversionValue; commitChanges = true; array[i] = value; } if (value < minValue || value > maxValue) { rasterHasVoids = true; if (null != missingDataSignal) { array[i] = missingDataSignal; commitChanges = true; } } } } if (commitChanges) bufferWrapper.putDouble(j * width, array, 0, width); } if (rasterHasVoids) { if (missingDataSignal != null) raster.setValue(AVKey.MISSING_DATA_SIGNAL, missingDataSignal); } else { raster.removeKey(AVKey.MISSING_DATA_SIGNAL); } raster.setValue(AVKey.ELEVATION_MIN, minValue); raster.setValue(AVKey.ELEVATION_MAX, maxValue); }