/******************************************************************************* * Copyright 2012 Geoscience Australia * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package au.gov.ga.earthsci.worldwind.common.layers.model.gocad; import gov.nasa.worldwind.geom.Position; import java.awt.Color; import java.net.URL; import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.media.opengl.GL2; import au.gov.ga.earthsci.worldwind.common.render.fastshape.FastShape; import au.gov.ga.earthsci.worldwind.common.util.ColorMap; /** * {@link GocadReader} implementation for reading TSurf GOCAD files. * * @author Michael de Hoog (michael.dehoog@ga.gov.au) */ public class GocadTSurfReader implements GocadReader<FastShape> { public final static String HEADER_REGEX = "(?i).*tsurf.*"; private final static Pattern trianglePattern = Pattern.compile("TRGL\\s+(\\d+)\\s+(\\d+)\\s+(\\d+).*"); private GocadReaderParameters parameters; private List<Position> positions; private List<Float> values; private float min, max; private List<Integer> triangleIds; private Color color; private ColorMap colorMap; private Map<Integer, Integer> vertexIdMap; private String name; private boolean zPositive = true; private String paintedVariableName; private int paintedVariableId = 0; private float noDataValue = -Float.MAX_VALUE; @Override public void begin(GocadReaderParameters parameters) { this.parameters = parameters; positions = new ArrayList<Position>(); values = new ArrayList<Float>(); min = Float.MAX_VALUE; max = -Float.MAX_VALUE; triangleIds = new ArrayList<Integer>(); vertexIdMap = new HashMap<Integer, Integer>(); paintedVariableName = parameters.getPaintedVariable(); } @Override public void addLine(String line) { Matcher matcher; matcher = vertexPattern.matcher(line); if (matcher.matches()) { int id = Integer.parseInt(matcher.group(1)); if (vertexIdMap.containsKey(id)) { throw new IllegalArgumentException("Duplicate vertex id: " + id); } double x = Double.parseDouble(matcher.group(2)); double y = Double.parseDouble(matcher.group(3)); double z = Double.parseDouble(matcher.group(4)); z = zPositive ? z : -z; if (parameters.getCoordinateTransformation() != null) { double[] transformed = new double[3]; parameters.getCoordinateTransformation().TransformPoint(transformed, x, y, z); x = transformed[0]; y = transformed[1]; z = transformed[2]; } Position position = Position.fromDegrees(y, x, z); vertexIdMap.put(id, positions.size()); positions.add(position); float value = Float.NaN; if (paintedVariableId <= 0) { value = (float) z; } else { double[] values = splitStringToDoubles(matcher.group(5)); if (paintedVariableId <= values.length) { value = (float) values[paintedVariableId - 1]; } } if (!Float.isNaN(value) && value != noDataValue) { min = Math.min(min, value); max = Math.max(max, value); } values.add(value); return; } matcher = atomPattern.matcher(line); if (matcher.matches()) { int id1 = Integer.parseInt(matcher.group(1)); int id2 = Integer.parseInt(matcher.group(2)); if (vertexIdMap.containsKey(id1)) { throw new IllegalArgumentException("Duplicate vertex id: " + id1); } if (!vertexIdMap.containsKey(id2)) { throw new IllegalArgumentException("Unknown vertex id: " + id2); } Position position = positions.get(vertexIdMap.get(id2)); vertexIdMap.put(id1, positions.size()); positions.add(position); float value = Float.NaN; if (paintedVariableId <= 0) { value = (float) position.elevation; } else { double[] values = splitStringToDoubles(matcher.group(3)); if (paintedVariableId <= values.length) { value = (float) values[paintedVariableId - 1]; } } if (!Float.isNaN(value) && value != noDataValue) { min = Math.min(min, value); max = Math.max(max, value); } values.add(value); return; } matcher = trianglePattern.matcher(line); if (matcher.matches()) { int t1 = Integer.parseInt(matcher.group(1)); int t2 = Integer.parseInt(matcher.group(2)); int t3 = Integer.parseInt(matcher.group(3)); triangleIds.add(t1); triangleIds.add(t2); triangleIds.add(t3); return; } if (!parameters.isColorInformationAvailable()) { matcher = solidColorPattern.matcher(line); if (matcher.matches()) { color = GocadColor.gocadLineToColor(line); return; } matcher = colormapColorsPattern.matcher(line); if (matcher.matches()) { colorMap = addColorsToColorMap(line); return; } matcher = colormapAlphaPattern.matcher(line); if (matcher.matches()) { colorMap = addAlphasToColorMap(line); return; } } matcher = namePattern.matcher(line); if (matcher.matches()) { name = matcher.group(1); return; } matcher = zpositivePattern.matcher(line); if (matcher.matches()) { zPositive = !matcher.group(1).equalsIgnoreCase("depth"); return; } matcher = paintedVariablePattern.matcher(line); if (matcher.matches()) { if (parameters.getPaintedVariable() == null) { paintedVariableName = matcher.group(1); } return; } matcher = propertiesPattern.matcher(line); if (matcher.matches()) { String properties = matcher.group(1).trim(); String[] split = properties.split("\\s+"); for (int i = 0; i < split.length; i++) { if (split[i].equalsIgnoreCase(paintedVariableName)) { paintedVariableId = i + 1; break; } } return; } matcher = nodataValuesPattern.matcher(line); if (matcher.matches()) { double[] values = splitStringToDoubles(matcher.group(1)); if (0 < paintedVariableId && paintedVariableId <= values.length) { noDataValue = (float) values[paintedVariableId - 1]; } return; } } @Override public FastShape end(URL context) { IntBuffer indicesBuffer = IntBuffer.allocate(triangleIds.size()); for (Integer i : triangleIds) { if (!vertexIdMap.containsKey(i)) { throw new IllegalArgumentException("Unknown vertex id: " + i); } indicesBuffer.put(vertexIdMap.get(i)); } if (name == null) { name = "TSurf"; } FastShape shape = new FastShape(positions, indicesBuffer.array(), GL2.GL_TRIANGLES); shape.setName(name); shape.setLighted(true); shape.setTwoSidedLighting(true); shape.setCalculateNormals(true); // Colouring priority is // (1) Colour map from layer def (parameters) // (2) Colour from layer def (parameters), // (3) Colour map from the GOCAD file // (4) Colour from the GOCAD file if (parameters.getColorMap() != null) { float[] colorBuffer = createColorBufferFromColorMap(parameters.getColorMap()); shape.setColorBufferElementSize(4); shape.setColorBuffer(colorBuffer); } else if (parameters.getColor() != null) { shape.setColor(parameters.getColor()); } else if (colorMap != null) { float[] colorBuffer = createColorBufferFromColorMap(colorMap); shape.setColorBufferElementSize(4); shape.setColorBuffer(colorBuffer); } else if (color != null) { shape.setColor(color); } return shape; } public static double[] splitStringToDoubles(String s) { String[] split = s.trim().split("[\\s,]+"); double[] array = new double[split.length]; int i = 0; for (String sp : split) { try { array[i++] = Double.parseDouble(sp); } catch (NumberFormatException e) { } } if (i == array.length) { return array; } return Arrays.copyOf(array, i); } private ColorMap addAlphasToColorMap(String line) { ColorMap result = this.colorMap; if (result == null) { result = new ColorMap(); result.setValuesPercentages(true); } // Format is [index alpha index alpha ...] double[] values = splitStringToDoubles(line.replace("*colormap*alphas:", "")); double maxIndex = values[values.length - 2]; for (int i = 0; i < values.length; i += 2) { double index = values[i] / maxIndex; double alpha = values[i + 1]; if (result.containsKey(index)) { Color existingColor = result.get(index); Color replacement = new Color(existingColor.getRed(), existingColor.getGreen(), existingColor.getBlue(), (int) (alpha * 255)); result.put(index, replacement); } else { Color newColor = new Color(0, 0, 0, (int) (alpha * 255)); result.put(index, newColor); } } return result; } private ColorMap addColorsToColorMap(String line) { ColorMap result = this.colorMap; if (result == null) { result = new ColorMap(); result.setValuesPercentages(true); } // Format is [index r g b index r g b ...] double[] values = splitStringToDoubles(line.replace("*colormap**colors:", "")); double maxIndex = values[values.length - 4]; for (int i = 0; i < values.length; i += 4) { double index = values[i] / maxIndex; int red = (int) (values[i + 1] * 255); int green = (int) (values[i + 2] * 255); int blue = (int) (values[i + 3] * 255); if (result.containsKey(index)) { Color existingColor = result.get(index); Color replacement = new Color(red, green, blue, existingColor.getAlpha()); result.put(index, replacement); } else { Color newColor = new Color(red, green, blue, 255); result.put(index, newColor); } } return result; } private float[] createColorBufferFromColorMap(ColorMap colorMap) { FloatBuffer colorBuffer = FloatBuffer.allocate(positions.size() * 4); for (float value : values) { if (Float.isNaN(value) || value == noDataValue) { colorBuffer.put(0).put(0).put(0).put(0); } else { Color color = colorMap.calculateColorNotingIsValuesPercentages(value, min, max); colorBuffer.put(color.getRed() / 255f).put(color.getGreen() / 255f).put(color.getBlue() / 255f) .put(color.getAlpha() / 255f); } } return colorBuffer.array(); } }