/* * Copyright (C) 2012 Dr. John Lindsay <jlindsay@uoguelph.ca> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package whitebox.geospatialfiles; import java.awt.Color; import java.io.File; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.nio.channels.FileChannel; import java.util.*; import whitebox.cartographic.PointMarkers.MarkerStyle; import whitebox.geospatialfiles.shapefile.attributes.DBFException; import whitebox.geospatialfiles.shapefile.attributes.AttributeTable; import whitebox.geospatialfiles.shapefile.ShapeFileRecord; import whitebox.geospatialfiles.shapefile.ShapeType; import whitebox.geospatialfiles.shapefile.*; import whitebox.geospatialfiles.shapefile.PolyLine; import whitebox.interfaces.MapLayer; import whitebox.structures.BoundingBox; import java.beans.PropertyChangeSupport; import java.io.IOException; import static whitebox.geospatialfiles.shapefile.ShapeType.*; import whitebox.utilities.StringUtilities; /** * * @author Dr. John Lindsay <jlindsay@uoguelph.ca> */ public class VectorLayerInfo implements MapLayer { public final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private String fileName; private ShapeFile shapefile; private int overlayNumber; private String paletteFile = ""; private String layerTitle = ""; private int alpha = 255; private double gamma = 1.0; private boolean visible = true; private BoundingBox fullExtent = null; private BoundingBox currentExtent = null; private float markerSize = 6.0f; private float lineThickness = 0.5f; private Color lineColour = Color.black; private Color fillColour = Color.red; private ShapeType shapeType; private boolean filled = true; private boolean outlined = true; private boolean dashed = false; private float[] dashArray = new float[]{4, 4, 12, 4}; private MarkerStyle markerStyle = MarkerStyle.CIRCLE; private String xyUnits = ""; private String[] attributeTableFields = new String[1]; private String paletteDirectory; private String pathSep; private boolean filledWithOneColour = true; private boolean outlinedWithOneColour = true; private boolean paletteScaled = false; private String colouringAttribute = ""; private int numPaletteEntries = 0; private int[] paletteData = null; private double minimumValue = -32768.0; private double maximumValue = -32768.0; private double displayMinValue = -32768.0; private double displayMaxValue = -32768.0; private double cartographicGeneralizationLevel = 0.5; private int selectedFeatureNumber = -1; private int maxDisplayedEntries = 5; private boolean visibleInLegend = true; private boolean isActivelyEdited = false; private boolean[] selectedFeatures; // Constructors public VectorLayerInfo() { } public VectorLayerInfo(String fileName, String paletteDirectory, int alpha, int overlayNumber) { this.fileName = fileName; File file = new File(fileName); if (!file.exists()) { System.out.println("File not found."); } this.paletteDirectory = paletteDirectory; this.layerTitle = file.getName().replace(".shp", ""); this.alpha = alpha; this.overlayNumber = overlayNumber; try { shapefile = new ShapeFile(fileName); } catch (IOException e) { // The files doesn't exist throw new IllegalArgumentException(); } currentExtent = new BoundingBox(shapefile.getxMin(), shapefile.getyMin(), shapefile.getxMax(), shapefile.getyMax()); fullExtent = currentExtent.clone(); shapeType = shapefile.getShapeType(); if (shapeType == ShapeType.POLYLINE && (layerTitle.toLowerCase().contains("roads") || layerTitle.toLowerCase().contains("transportation"))) { lineColour = Color.black; } else if (shapeType == ShapeType.POLYLINE && (layerTitle.toLowerCase().contains("stream") || layerTitle.toLowerCase().contains("river") || layerTitle.toLowerCase().contains("water") || layerTitle.toLowerCase().contains("hydrology"))) { lineColour = new Color(51, 153, 255);//Color.blue; } else if (shapeType == ShapeType.POLYGON && (layerTitle.toLowerCase().contains("lake") || layerTitle.toLowerCase().contains("water"))) { lineColour = new Color(51, 153, 255); fillColour = new Color(230, 255, 255);//new Color(153, 204, 255); } else if (shapeType.getBaseType() == ShapeType.POLYLINE) { //lineColour = Color.RED; // new Color(153, 204, 255); Random generator = new Random(); int r = (int) (255 * generator.nextFloat()); int g = (int) (255 * generator.nextFloat()); int b = (int) (255 * generator.nextFloat()); lineColour = new Color(r, g, b); } else if (shapeType.getBaseType() == ShapeType.POLYGON) { lineColour = Color.black; //fillColour = new Color(153, 204, 255); // set the fill colour to a random light colour Random generator = new Random(); int r = (int) (100 + 155 * generator.nextFloat()); int g = (int) (100 + 155 * generator.nextFloat()); int b = (int) (100 + 155 * generator.nextFloat()); fillColour = new Color(r, g, b); } else if (shapeType.getBaseType() == ShapeType.POINT || shapeType.getBaseType() == ShapeType.MULTIPOINT) { Random generator = new Random(); int r = (int) (255 * generator.nextFloat()); int g = (int) (255 * generator.nextFloat()); int b = (int) (255 * generator.nextFloat()); fillColour = new Color(r, g, b); if (shapefile.getNumberOfRecords() > 10000 || shapeType.getBaseType() == ShapeType.MULTIPOINT) { outlined = false; markerSize = 3.0f; } } this.xyUnits = shapefile.getXYUnits(); this.attributeTableFields = shapefile.getAttributeTableFields(); pathSep = File.separator; try { String applicationDirectory = java.net.URLDecoder.decode(getClass().getProtectionDomain().getCodeSource().getLocation().getPath(), "UTF-8"); if (applicationDirectory.endsWith(".exe") || applicationDirectory.endsWith(".jar")) { applicationDirectory = new File(applicationDirectory).getParent(); } else { // Add the path to the class files applicationDirectory += getClass().getName().replace('.', File.separatorChar); // Step one level up as we are only interested in the // directory containing the class files applicationDirectory = new File(applicationDirectory).getParent(); } findPaletteDirectory(new File(applicationDirectory)); paletteFile = paletteDirectory + "categorical4.pal"; } catch (Exception e) { } selectedFeatures = new boolean[shapefile.getNumberOfRecords() + 1]; } public void refreshAttributeTable() { shapefile.refreshAttributeTable(); this.attributeTableFields = shapefile.getAttributeTableFields(); } public int getAlpha() { return alpha; } public void setAlpha(int value) { if (value < 0) { value = 0; } if (value > 255) { value = 255; } alpha = value; //setRecordsColourData(); } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public double getNonlinearity() { return gamma; } public void setNonlinearity(double value) { gamma = value; } public String getPaletteFile() { return paletteFile; } public void setPaletteFile(String fileName) { if (!paletteFile.equals(fileName)) { paletteFile = fileName; } } public ShapeType getShapeType() { return shapeType; } ArrayList<ShapeFileRecord> recs; public ArrayList<ShapeFileRecord> getData() { return recs; } public float getMarkerSize() { return markerSize; } public void setMarkerSize(float markerSize) { this.markerSize = markerSize; } public float getLineThickness() { return lineThickness; } public void setLineThickness(float lineThickness) { this.lineThickness = lineThickness; } public Color getFillColour() { return fillColour; } public void setFillColour(Color fillColour) { this.fillColour = fillColour; //setRecordsColourData(); } public ShapeFile getShapefile() { return shapefile; } public Color getLineColour() { return lineColour; } public void setLineColour(Color lineColour) { this.lineColour = lineColour; //setRecordsColourData(); } public boolean isFilled() { return filled; } public void setFilled(boolean filled) { this.filled = filled; } public boolean isOutlined() { return outlined; } public void setOutlined(boolean outlined) { this.outlined = outlined; } public void setMarkerStyle(MarkerStyle markerStyle) { this.markerStyle = markerStyle; } public MarkerStyle getMarkerStyle() { return markerStyle; } public String getXYUnits() { return xyUnits; } public void setXYUnits(String xyUnits) { this.xyUnits = xyUnits; } public String[] getAttributeTableFields() { return attributeTableFields; } public double getCartographicGeneralizationLevel() { return cartographicGeneralizationLevel; } boolean generalizationLevelDirty = false; public void setCartographicGeneralizationLevel(double generalizeLevel) { cartographicGeneralizationLevel = generalizeLevel; generalizationLevelDirty = true; } public int getMaxDisplayedEntries() { return maxDisplayedEntries; } public void setMaxDisplayedEntries(int maxDisplayedEntries) { this.maxDisplayedEntries = maxDisplayedEntries; } public boolean isActivelyEdited() { return isActivelyEdited; } public void setActivelyEdited(boolean activelyEdited) { this.isActivelyEdited = activelyEdited; if (!activelyEdited) { try { shapefile.write(); } catch (Exception e) { System.out.println(e.getMessage()); } } } @Override public String getLayerTitle() { return layerTitle; } @Override public void setLayerTitle(String title) { layerTitle = title; } @Override public MapLayer.MapLayerType getLayerType() { return MapLayer.MapLayerType.VECTOR; } @Override public BoundingBox getFullExtent() { return fullExtent.clone(); } public BoundingBox getSelectedExtent() { if (selectedFeatureNumbers.isEmpty()) { return null; } double minX = Double.POSITIVE_INFINITY; double minY = Double.POSITIVE_INFINITY; double maxX = Double.NEGATIVE_INFINITY; double maxY = Double.NEGATIVE_INFINITY; for (Integer i : selectedFeatureNumbers) { BoundingBox recBox = shapefile.getRecord(i - 1).getGeometry().getBox(); if (recBox.getMinX() < minX) { minX = recBox.getMinX(); } if (recBox.getMaxX() > maxX) { maxX = recBox.getMaxX(); } if (recBox.getMinY() < minY) { minY = recBox.getMinY(); } if (recBox.getMaxY() > maxY) { maxY = recBox.getMaxY(); } } double dx = maxX - minX; double dy = maxY - minY; if (dx > 0 && dy > 0) { minX -= dx * 0.05; maxX += dx * 0.05; minY -= dy * 0.05; maxY += dy * 0.05; return new BoundingBox(minX, minY, maxX, maxY); } else { // it's a single point dx = fullExtent.getMaxX() - fullExtent.getMinX(); dy = fullExtent.getMaxY() - fullExtent.getMinY(); minX -= dx * 0.05; maxX += dx * 0.05; minY -= dy * 0.05; maxY += dy * 0.05; return new BoundingBox(minX, minY, maxX, maxY); } } @Override public BoundingBox getCurrentExtent() { return currentExtent.clone(); } @Override public final void setCurrentExtent(BoundingBox bb) { if (!bb.equals(currentExtent)) { currentExtent = bb.clone(); } } public final void setCurrentExtent(BoundingBox bb, double minSize) { if (!bb.equals(currentExtent) || recs == null || generalizationLevelDirty) { currentExtent = bb.clone(); recs = shapefile.getRecordsInBoundingBox(currentExtent, minSize); } generalizationLevelDirty = false; } @Override public boolean isVisible() { return visible; } @Override public void setVisible(boolean value) { visible = value; } @Override public int getOverlayNumber() { return overlayNumber; } @Override public void setOverlayNumber(int value) { overlayNumber = value; } public boolean isDashed() { return dashed; } public void setDashed(boolean dashed) { this.dashed = dashed; } public float[] getDashArray() { return dashArray; } public void setDashArray(float[] dashArray) { this.dashArray = dashArray; } public boolean isFilledWithOneColour() { return filledWithOneColour; } public void setFilledWithOneColour(boolean filledWithOneColour) { this.filledWithOneColour = filledWithOneColour; } public boolean isOutlinedWithOneColour() { return outlinedWithOneColour; } public void setOutlinedWithOneColour(boolean outlinedWithOneColour) { this.outlinedWithOneColour = outlinedWithOneColour; } public boolean isPaletteScaled() { return paletteScaled; } public void setPaletteScaled(boolean paletteScaled) { this.paletteScaled = paletteScaled; } public String getFillAttribute() { return colouringAttribute; } /** * Sets the attribute within the shapefile dbf that is used for setting * fill/line colours. * * @param fillAttribute Name of the attribute. */ public void setFillAttribute(String fillAttribute) { this.colouringAttribute = fillAttribute; // minimumValue = -32768.0; // maximumValue = -32768.0; // displayMinValue = -32768.0; // displayMaxValue = -32768.0; updateMinAndMaxForAttribute(fillAttribute); //setRecordsColourData(); } public void updateMinAndMaxForAttribute(String attribute) { try { // first find out if the attribute is a numeric data type AttributeTable table = shapefile.getAttributeTable(); byte datatype = table.getFieldType(attribute); if (datatype == 'N' || datatype == 'F') { double[] data = readDataForAttribute(attribute); minimumValue = Double.POSITIVE_INFINITY; maximumValue = Double.NEGATIVE_INFINITY; for (int r = 0; r < data.length; r++) { if (data[r] < minimumValue) { minimumValue = data[r]; } if (data[r] > maximumValue) { maximumValue = data[r]; } } displayMinValue = minimumValue; displayMaxValue = maximumValue; } else { minimumValue = -32768.0; maximumValue = -32768.0; displayMinValue = -32768.0; displayMaxValue = -32768.0; } } catch (Exception e) { minimumValue = -32768.0; maximumValue = -32768.0; displayMinValue = -32768.0; displayMaxValue = -32768.0; } } private double[] readDataForAttribute(String attribute) { try { if (attribute.toLowerCase().contains("feature z")) { int numRecords = shapefile.getNumberOfRecords(); double[] data = new double[numRecords]; int a; switch (shapefile.getShapeType()) { case POINTZ: for (a = 0; a < numRecords; a++) { double[] zArray = ((PointZ) (shapefile.getRecord(a).getGeometry())).getzArray(); data[a] = zArray[0]; } break; case MULTIPOINTZ: int k = 0; for (a = 0; a < shapefile.getNumberOfRecords(); a++) { double[] zArray = ((MultiPointZ) (shapefile.getRecord(a).getGeometry())).getzArray(); for (double v : zArray) { data[k] = v; k++; } } break; case POLYLINEZ: for (a = 0; a < numRecords; a++) { double[] zArray = ((PolyLineZ) (shapefile.getRecord(a).getGeometry())).getzArray(); data[a] = zArray[0]; } break; case POLYGONZ: for (a = 0; a < numRecords; a++) { double[] zArray = ((PolygonZ) (shapefile.getRecord(a).getGeometry())).getzArray(); data[a] = zArray[0]; } break; } return data; } else if (attribute.toLowerCase().contains("feature measure")) { int k = 0; int numRecords = shapefile.getNumberOfRecords(); double[] data = new double[numRecords]; int a; switch (shapefile.getShapeType()) { case POINTZ: for (a = 0; a < numRecords; a++) { double[] zArray = ((PointZ) (shapefile.getRecord(a).getGeometry())).getmArray(); data[a] = zArray[0]; } break; case MULTIPOINTZ: for (a = 0; a < shapefile.getNumberOfRecords(); a++) { double[] zArray = ((MultiPointZ) (shapefile.getRecord(a).getGeometry())).getmArray(); for (double v : zArray) { data[k] = v; k++; } } break; case POLYLINEZ: for (a = 0; a < numRecords; a++) { double[] zArray = ((PolyLineZ) (shapefile.getRecord(a).getGeometry())).getmArray(); data[a] = zArray[0]; } break; case POLYGONZ: for (a = 0; a < numRecords; a++) { double[] zArray = ((PolygonZ) (shapefile.getRecord(a).getGeometry())).getmArray(); data[a] = zArray[0]; } break; case POINTM: for (a = 0; a < numRecords; a++) { double[] zArray = {((PointM) (shapefile.getRecord(a).getGeometry())).getM()}; data[a] = zArray[0]; } break; case MULTIPOINTM: for (a = 0; a < shapefile.getNumberOfRecords(); a++) { double[] zArray = ((MultiPointM) (shapefile.getRecord(a).getGeometry())).getmArray(); for (double v : zArray) { data[k] = v; k++; } } break; case POLYLINEM: for (a = 0; a < numRecords; a++) { double[] zArray = ((PolyLineM) (shapefile.getRecord(a).getGeometry())).getmArray(); data[a] = zArray[0]; } break; case POLYGONM: for (a = 0; a < numRecords; a++) { double[] zArray = ((PolygonM) (shapefile.getRecord(a).getGeometry())).getmArray(); data[a] = zArray[0]; } break; } return data; } else { // first find out if the attribute is a numeric data type AttributeTable table = shapefile.getAttributeTable(); byte datatype = table.getFieldType(attribute); if (datatype == 'N' || datatype == 'F') { minimumValue = Double.POSITIVE_INFINITY; maximumValue = Double.NEGATIVE_INFINITY; int numRecords = table.getNumberOfRecords(); double[] data = new double[numRecords]; for (int r = 0; r < numRecords; r++) { data[r] = (double) table.getValue(r, attribute); } displayMinValue = minimumValue; displayMaxValue = maximumValue; return data; } else { return null; } } } catch (Exception e) { return null; } } public void clipLowerTailForDisplayMinimum(double percent) { if (percent > 100 || percent < 0) { return; } double[] data = readDataForAttribute(this.colouringAttribute); int numPointRecords = data.length; int target = (int) (numPointRecords * percent / 100d); if (target >= numPointRecords) { displayMinValue = maximumValue; return; } if (target <= 0) { displayMinValue = minimumValue; return; } Arrays.parallelSort(data); displayMinValue = data[target]; } public void clipUpperTailForDisplayMaximum(double percent) { percent = 100 - percent; if (percent > 100 || percent < 0) { return; } double[] data = readDataForAttribute(this.colouringAttribute); int numPointRecords = data.length; int target = (int) (numPointRecords * percent / 100d); if (target >= numPointRecords) { displayMaxValue = maximumValue; return; } if (target <= 0) { displayMaxValue = minimumValue; return; } Arrays.parallelSort(data); displayMaxValue = data[target]; } public String getLineAttribute() { return colouringAttribute; } public void setLineAttribute(String lineAttribute) { this.colouringAttribute = lineAttribute; updateMinAndMaxForAttribute(lineAttribute); // minimumValue = -32768.0; // maximumValue = -32768.0; // displayMinValue = -32768.0; // displayMaxValue = -32768.0; //setRecordsColourData(); } private Color[] colourData; public Color[] getColourData() { if (colourData == null) { setRecordsColourData(); } return colourData; } public VectorLayerInfo.LegendEntry[] getLegendEntries() { return legendEntries; } public double getMaximumValue() { return maximumValue; } // public void setMaximumValue(double value) { // this.maximumValue = value; // } public double getMinimumValue() { return minimumValue; } // public void setMinimumValue(double value) { // this.minimumValue = value; // } public double getDisplayMaxValue() { return displayMaxValue; } public void setDisplayMaxValue(double value) { this.displayMaxValue = value; } public double getDisplayMinValue() { return displayMinValue; } public void setDisplayMinValue(double value) { this.displayMinValue = value; } /** * Sets a specified record number as selected. * * @param recordNumber The one-based record ID. */ public void setSelectedFeature(int recordNumber) { if (selectedFeatures[recordNumber]) { deselectFeature(recordNumber); } else { selectedFeatures[recordNumber] = true; selectedFeatureNumbers.add(recordNumber); } //int oldValue = this.selectedFeatureNumber; this.selectedFeatureNumber = recordNumber; this.pcs.firePropertyChange("selectedFeatureNumber", -1, selectedFeatureNumber); } /** * Deselects a feature from a vector layer. * * @param recordNumber the record index number */ public void deselectFeature(int recordNumber) { if (selectedFeatures[recordNumber]) { selectedFeatures[recordNumber] = false; // remove it from the list of selected features selectedFeatureNumbers.remove(new Integer(recordNumber)); this.pcs.firePropertyChange("selectedFeatureNumber", -2, -1); } } /** * Clears all of the selected features from a vector layer. */ public void clearSelectedFeatures() { selectedFeatures = new boolean[shapefile.getNumberOfRecords() + 1]; selectedFeatureNumbers.clear(); // int oldValue = this.selectedFeatureNumber; this.selectedFeatureNumber = -2; this.pcs.firePropertyChange("selectedFeatureNumber", -1, selectedFeatureNumber); } /** * Deletes all of the selected features from the shapefile. <b><i>This * action cannot be reversed</i></b>. */ public void deleteSelectedFeatures() { // sort the list of selected features Collections.sort(selectedFeatureNumbers); for (int i = selectedFeatureNumbers.size() - 1; i >= 0; i--) { shapefile.deleteRecord(selectedFeatureNumbers.get(i)); } clearSelectedFeatures(); } /** * Determines if a specified record number is selected. * * @param recordNumber The one-based record ID. * @return boolean A boolean value. */ public boolean isFeatureSelected(int recordNumber) { if (recordNumber >= selectedFeatures.length) { return false; } return selectedFeatures[recordNumber]; } private ArrayList<Integer> selectedFeatureNumbers = new ArrayList<>(); /** * Gets a list of all selected feature record numbers. * * @return ArrayList of selected feature numbers. */ public ArrayList<Integer> getSelectedFeatureNumbers() { return selectedFeatureNumbers; } /** * Returns the number of features that are currently selected. * * @return int of the number of selected features. */ public int getNumSelectedFeatures() { return selectedFeatureNumbers.size(); } // public int getSelectedFeatureNumber() { // return selectedFeatureNumber; // } // // public void setSelectedFeatureNumber(int selectedFeatureNumber) { // int oldValue = this.selectedFeatureNumber; // this.selectedFeatureNumber = selectedFeatureNumber; // this.pcs.firePropertyChange("selectedFeatureNumber", oldValue, selectedFeatureNumber); // } private VectorLayerInfo.LegendEntry[] legendEntries; public void setRecordsColourData() { int numRecords = shapefile.getNumberOfRecords(); if (shapefile.getShapeType().getBaseType() == ShapeType.MULTIPOINT) { // if (colouringAttribute.toLowerCase().contains("feature z") || // colouringAttribute.toLowerCase().contains("feature measure")) { numRecords = 0; for (ShapeFileRecord rec : shapefile.records) { numRecords += rec.getGeometry().getPoints().length; } // } } int entryNum, a, i; int r1, g1, b1; int a1 = this.getAlpha(); //double nullDataFlag = Integer.MIN_VALUE; Color legendColour; colourData = new Color[numRecords]; boolean singleColour = true; Color clr; if (shapeType == ShapeType.POLYLINE || shapeType == ShapeType.POLYLINEM || shapeType == ShapeType.POLYLINEZ) { singleColour = outlinedWithOneColour; clr = lineColour; } else { singleColour = filledWithOneColour; clr = fillColour; } if (singleColour) { clr = new Color(clr.getRed(), clr.getGreen(), clr.getBlue(), a1); for (i = 0; i < numRecords; i++) { colourData[i] = clr; } legendEntries = new VectorLayerInfo.LegendEntry[1]; legendEntries[0] = new VectorLayerInfo.LegendEntry(this.layerTitle, clr); } else { // read the palette data readPalette(); // find the data type of the fill attribute String dbfFileName = fileName.replace(".shp", ".dbf"); try { byte dataType = 'N'; Object[][] data = new Object[numRecords][3]; if (colouringAttribute.toLowerCase().contains("feature z")) { switch (shapefile.getShapeType()) { case POINTZ: for (a = 0; a < numRecords; a++) { data[a][0] = a; double[] zArray = ((PointZ) (shapefile.getRecord(a).getGeometry())).getzArray(); data[a][1] = zArray[0]; } break; case MULTIPOINTZ: int k = 0; for (a = 0; a < shapefile.getNumberOfRecords(); a++) { data[a][0] = a; double[] zArray = ((MultiPointZ) (shapefile.getRecord(a).getGeometry())).getzArray(); for (double v : zArray) { data[k][1] = v; k++; } } break; case POLYLINEZ: for (a = 0; a < numRecords; a++) { data[a][0] = a; double[] zArray = ((PolyLineZ) (shapefile.getRecord(a).getGeometry())).getzArray(); data[a][1] = zArray[0]; } break; case POLYGONZ: for (a = 0; a < numRecords; a++) { data[a][0] = a; double[] zArray = ((PolygonZ) (shapefile.getRecord(a).getGeometry())).getzArray(); data[a][1] = zArray[0]; } break; } } else if (colouringAttribute.toLowerCase().contains("feature measure")) { int k = 0; switch (shapefile.getShapeType()) { case POINTZ: for (a = 0; a < numRecords; a++) { data[a][0] = a; double[] zArray = ((PointZ) (shapefile.getRecord(a).getGeometry())).getmArray(); data[a][1] = zArray[0]; } break; case MULTIPOINTZ: for (a = 0; a < shapefile.getNumberOfRecords(); a++) { data[a][0] = a; double[] zArray = ((MultiPointZ) (shapefile.getRecord(a).getGeometry())).getmArray(); for (double v : zArray) { data[k][1] = v; k++; } } break; case POLYLINEZ: for (a = 0; a < numRecords; a++) { data[a][0] = a; double[] zArray = ((PolyLineZ) (shapefile.getRecord(a).getGeometry())).getmArray(); data[a][1] = zArray[0]; } break; case POLYGONZ: for (a = 0; a < numRecords; a++) { data[a][0] = a; double[] zArray = ((PolygonZ) (shapefile.getRecord(a).getGeometry())).getmArray(); data[a][1] = zArray[0]; } break; case POINTM: for (a = 0; a < numRecords; a++) { data[a][0] = a; double[] zArray = {((PointM) (shapefile.getRecord(a).getGeometry())).getM()}; data[a][1] = zArray[0]; } break; case MULTIPOINTM: for (a = 0; a < shapefile.getNumberOfRecords(); a++) { data[a][0] = a; double[] zArray = ((MultiPointM) (shapefile.getRecord(a).getGeometry())).getmArray(); for (double v : zArray) { data[k][1] = v; k++; } } break; case POLYLINEM: for (a = 0; a < numRecords; a++) { data[a][0] = a; double[] zArray = ((PolyLineM) (shapefile.getRecord(a).getGeometry())).getmArray(); data[a][1] = zArray[0]; } break; case POLYGONM: for (a = 0; a < numRecords; a++) { data[a][0] = a; double[] zArray = ((PolygonM) (shapefile.getRecord(a).getGeometry())).getmArray(); data[a][1] = zArray[0]; } break; } } else { AttributeTable table = new AttributeTable(dbfFileName); int fieldNum = table.getFieldColumnNumberFromName(colouringAttribute); dataType = table.getFieldType(fieldNum); // read the records // Object[][] data = new Object[numRecords][3]; Object val; for (a = 0; a < numRecords; a++) { data[a][0] = a; val = table.getValue(a, fieldNum); data[a][1] = val; } } // Object[] rec; // a = 0; // while ((rec = table.nextRecord()) != null) { // data[a][0] = a; // if (rec[fieldNum] != null) { // data[a][1] = rec[fieldNum]; // } else { // data[a][1] = null; // } // a++; // } if (!(dataType == 'N') && !(dataType == 'F') && !(dataType == 'L')) { // sort the data based on the field data Arrays.sort(data, (final Object[] entry1, final Object[] entry2) -> { if (entry1[1] instanceof String) { final String val1 = (String) entry1[1]; final String val2 = (String) entry2[1]; // see if the strings starts with a number String[] val1Array = val1.split(" "); String[] val2Array = val2.split(" "); if (StringUtilities.isNumeric(val1Array[0]) && StringUtilities.isNumeric(val2Array[0])) { return Double.valueOf(val1Array[0]).compareTo(Double.valueOf(val2Array[0])); } else { return String.valueOf(val1).compareTo(val2); } } else if (entry1[1] instanceof Integer) { final int val1 = (Integer) entry1[1]; final int val2 = (Integer) entry2[1]; return Integer.valueOf(val1).compareTo(val2); } else if (entry1[1] instanceof Double) { final double val1 = (Double) entry1[1]; final double val2 = (Double) entry2[1]; return Double.valueOf(val1).compareTo(val2); } else if (entry1[1] instanceof Float) { final float val1 = (Float) entry1[1]; final float val2 = (Float) entry2[1]; return Float.valueOf(val1).compareTo(val2); } else if (entry1[1] instanceof Date) { final Date val1 = (Date) entry1[1]; final Date val2 = (Date) entry2[1]; return val1.compareTo(val2); } else { final double val1 = (Double) entry1[1]; final double val2 = (Double) entry2[1]; return Double.valueOf(val1).compareTo(val2); } }); int clrNumber = 0; data[0][2] = clrNumber; if (numRecords > 1) { for (i = 1; i < numRecords; i++) { if (data[i][1].equals(data[i - 1][1])) { data[i][2] = clrNumber; } else { clrNumber++; data[i][2] = clrNumber; } } } // figure out the legend entries. legendEntries = new VectorLayerInfo.LegendEntry[clrNumber + 1]; entryNum = (Integer) (data[0][2]) % numPaletteEntries; int offset = 0; if (data[0][1] == null || String.valueOf(data[0][1]).trim().isEmpty()) { legendEntries[0] = new VectorLayerInfo.LegendEntry("Not Specified", new Color(255, 255, 255, 0)); offset = -1; } else { legendEntries[0] = new VectorLayerInfo.LegendEntry(String.valueOf(data[0][1]), new Color(paletteData[entryNum])); } int legendEntryNum = 1; double maxValue = legendEntries.length - 1; if (numRecords > 1) { for (i = 1; i < numRecords; i++) { if (!data[i][1].equals(data[i - 1][1])) { if (!paletteScaled) { entryNum = (Integer) (data[i][2]) % numPaletteEntries; } else { entryNum = (int) (((Integer) data[i][2] / maxValue) * (numPaletteEntries - 1)); } if (data[i][1] == null || String.valueOf(data[i][1]).trim().isEmpty()) { legendEntries[legendEntryNum] = new VectorLayerInfo.LegendEntry("Not Specified", new Color(255, 255, 255, 0)); } else { legendEntries[legendEntryNum] = new VectorLayerInfo.LegendEntry(String.valueOf(data[i][1]), new Color(paletteData[entryNum + offset])); } legendEntryNum++; } } } // sort it back into the record number order. Arrays.parallelSort(data, (final Object[] entry1, final Object[] entry2) -> { final int int1 = (Integer) entry1[0]; final int int2 = (Integer) entry2[0]; return Integer.valueOf(int1).compareTo(int2); }); // fill the colourData array. if (!paletteScaled) { for (i = 0; i < numRecords; i++) { if (data[i][1] != null && !String.valueOf(data[i][1]).trim().isEmpty()) { clrNumber = (int) (data[i][2]) + offset; entryNum = clrNumber % numPaletteEntries; legendColour = new Color(paletteData[entryNum]); r1 = legendColour.getRed(); g1 = legendColour.getGreen(); b1 = legendColour.getBlue(); colourData[i] = new Color(r1, g1, b1, a1); } else { colourData[i] = new Color(255, 255, 255, 0); } } } else { // the palette is scaled int value = 0; int numPaletteEntriesLessOne = numPaletteEntries - 1; //double maxValue = legendEntries.length - 1; for (i = 0; i < numRecords; i++) { if (data[i][1] != null) { value = (Integer) data[i][2]; if (gamma == 1) { entryNum = (int) ((value / maxValue) * numPaletteEntriesLessOne); } else { entryNum = (int) (Math.pow((value / maxValue), gamma) * numPaletteEntriesLessOne); } legendColour = new Color(paletteData[entryNum]); r1 = legendColour.getRed(); g1 = legendColour.getGreen(); b1 = legendColour.getBlue(); colourData[i] = new Color(r1, g1, b1, a1); } else { colourData[i] = new Color(255, 255, 255, 0); } } } } else if (!(dataType == 'L')) { // it's a number if (paletteScaled) { // it's a numerical field legendEntries = new VectorLayerInfo.LegendEntry[1]; legendEntries[0] = new VectorLayerInfo.LegendEntry("continuous numerical variable", Color.black); if (numRecords > 1) { if (minimumValue == -32768.0 && maximumValue == -32768.0) { // find the min and max values double minValue = Float.POSITIVE_INFINITY; double maxValue = Float.NEGATIVE_INFINITY; for (i = 0; i < numRecords; i++) { if (data[i][1] == null) { // do nothing } else { if ((Double) data[i][1] > maxValue) { maxValue = (Double) data[i][1]; } } if (data[i][1] == null) { // do nothing } else { if ((Double) data[i][1] < minValue) { minValue = (Double) data[i][1]; } } } minimumValue = minValue; maximumValue = maxValue; if (displayMinValue == -32768.0 && displayMaxValue == -32768.0) { displayMinValue = minValue; displayMaxValue = maxValue; } } double range = displayMaxValue - displayMinValue; double value; int numPaletteEntriesLessOne = numPaletteEntries - 1; for (i = 0; i < numRecords; i++) { if (data[i][1] == null) { colourData[i] = new Color(255, 255, 255, 0); } else { value = (Double) data[i][1]; if (gamma == 1) { entryNum = (int) ((value - displayMinValue) / range * numPaletteEntriesLessOne); } else { entryNum = (int) (Math.pow((value - displayMinValue) / range, gamma) * numPaletteEntriesLessOne); } //entryNum = (int) (((value - minimumValue) / range) * (numPaletteEntries - 1)); if (entryNum < 0) { entryNum = 0; } if (entryNum > numPaletteEntries - 1) { entryNum = numPaletteEntries - 1; } clr = new Color(paletteData[entryNum]); colourData[i] = new Color(clr.getRed(), clr.getGreen(), clr.getBlue(), a1); } } } else { colourData[0] = new Color(paletteData[0]); } } else { // it's not scaled // sort the data based on the field data Arrays.sort(data, new Comparator<Object[]>() { @Override public int compare(final Object[] entry1, final Object[] entry2) { // still need to handle date and boolean data types. if (entry1[1] instanceof Integer) { final int val1 = (Integer) entry1[1]; final int val2 = (Integer) entry2[1]; return Integer.valueOf(val1).compareTo(val2); } else if (entry1[1] instanceof Double) { final double val1 = (Double) entry1[1]; final double val2 = (Double) entry2[1]; return Double.valueOf(val1).compareTo(val2); } else if (entry1[1] instanceof Float) { final float val1 = (Float) entry1[1]; final float val2 = (Float) entry2[1]; return Float.valueOf(val1).compareTo(val2); } else if (entry1[1] instanceof String) { final String val1 = (String) entry1[1]; final String val2 = (String) entry2[1]; return String.valueOf(val1).compareTo(val2); } else if (entry1[1] instanceof Date) { final Date val1 = (Date) entry1[1]; final Date val2 = (Date) entry2[1]; return val1.compareTo(val2); } else { final double val1 = (Double) entry1[1]; final double val2 = (Double) entry2[1]; return Double.valueOf(val1).compareTo(val2); } } }); int clrNumber = 0; data[0][2] = clrNumber; if (numRecords > 1) { for (i = 1; i < numRecords; i++) { if (data[i][1].equals(data[i - 1][1])) { data[i][2] = clrNumber; } else { clrNumber++; data[i][2] = clrNumber; } } } // figure out the legend entries. legendEntries = new VectorLayerInfo.LegendEntry[clrNumber + 1]; entryNum = (Integer) (data[0][2]) % numPaletteEntries; if (data[0][1] == null) { legendEntries[0] = new VectorLayerInfo.LegendEntry("Null", new Color(255, 255, 255, 0)); } else { legendEntries[0] = new VectorLayerInfo.LegendEntry(String.valueOf(data[0][1]), new Color(paletteData[entryNum])); } int legendEntryNum = 1; if (numRecords > 1) { for (i = 1; i < numRecords; i++) { if (!data[i][1].equals(data[i - 1][1])) { entryNum = (Integer) (data[i][2]) % numPaletteEntries; if (data[i][1] == null) { legendEntries[legendEntryNum] = new VectorLayerInfo.LegendEntry("Null", new Color(255, 255, 255, 0)); } else { legendEntries[legendEntryNum] = new VectorLayerInfo.LegendEntry(String.valueOf(data[i][1]), new Color(paletteData[entryNum])); } legendEntryNum++; } } } // sort it back into the record number order. Arrays.sort(data, new Comparator<Object[]>() { @Override public int compare(final Object[] entry1, final Object[] entry2) { final int int1 = (Integer) entry1[0]; final int int2 = (Integer) entry2[0]; return Integer.valueOf(int1).compareTo(int2); } }); // fill the colourData array. for (i = 0; i < numRecords; i++) { if (data[i][1] == null) { colourData[i] = new Color(255, 255, 255, 0); } else { clrNumber = (Integer) (data[i][2]); entryNum = clrNumber % numPaletteEntries; legendColour = new Color(paletteData[entryNum]); r1 = legendColour.getRed(); g1 = legendColour.getGreen(); b1 = legendColour.getBlue(); colourData[i] = new Color(r1, g1, b1, a1); } } } } else { // it's a boolean legendEntries = new VectorLayerInfo.LegendEntry[2]; if (paletteScaled) { int numPaletteEntriesLessOne = numPaletteEntries - 1; legendEntries[0] = new VectorLayerInfo.LegendEntry("False", new Color(paletteData[0])); legendEntries[1] = new VectorLayerInfo.LegendEntry("True", new Color(paletteData[numPaletteEntriesLessOne])); boolean value; for (i = 0; i < numRecords; i++) { if (data[i][1] == null) { colourData[i] = new Color(255, 255, 255, 0); } else { value = (boolean) data[i][1]; if (value) { legendColour = new Color(paletteData[numPaletteEntriesLessOne]); } else { legendColour = new Color(paletteData[0]); } r1 = legendColour.getRed(); g1 = legendColour.getGreen(); b1 = legendColour.getBlue(); colourData[i] = new Color(r1, g1, b1, a1); } } } else { legendEntries[0] = new VectorLayerInfo.LegendEntry("False", new Color(paletteData[0])); legendEntries[1] = new VectorLayerInfo.LegendEntry("True", new Color(paletteData[1])); boolean value; for (i = 0; i < numRecords; i++) { if (data[i][1] == null) { colourData[i] = new Color(255, 255, 255, 0); } else { value = (boolean) data[i][1]; if (value) { legendColour = new Color(paletteData[1]); } else { legendColour = new Color(paletteData[0]); } r1 = legendColour.getRed(); g1 = legendColour.getGreen(); b1 = legendColour.getBlue(); colourData[i] = new Color(r1, g1, b1, a1); } } } } } catch (DBFException dbfe) { System.out.println(dbfe.getMessage()); } catch (Exception e) { System.out.println(e.getMessage()); } } } private void findPaletteDirectory(File dir) { File[] files = dir.listFiles(); for (int x = 0; x < files.length; x++) { if (files[x].isDirectory()) { if (files[x].toString().endsWith(pathSep + "palettes")) { paletteDirectory = files[x].toString() + pathSep; break; } else { findPaletteDirectory(files[x]); } } } } private void readPalette() { RandomAccessFile rIn = null; ByteBuffer buf = null; int i; try { // see if the file exists, if not, set it to the default palette. File file = new File(paletteFile); if (!file.exists()) { return; } numPaletteEntries = (int) (file.length() / 4); buf = ByteBuffer.allocate(numPaletteEntries * 4); rIn = new RandomAccessFile(paletteFile, "r"); FileChannel inChannel = rIn.getChannel(); inChannel.position(0); inChannel.read(buf); // Check the byte order. buf.order(ByteOrder.LITTLE_ENDIAN); // Read the data. buf.rewind(); IntBuffer ib = buf.asIntBuffer(); paletteData = new int[numPaletteEntries]; ib.get(paletteData); ib = null; // Update the palette for the alpha value. if (alpha < 255) { int r, g, b, val; for (i = 0; i < numPaletteEntries; i++) { val = paletteData[i]; r = (val >> 16) & 0xFF; g = (val >> 8) & 0xFF; b = val & 0xFF; paletteData[i] = (alpha << 24) | (r << 16) | (g << 8) | b; } } } catch (Exception e) { System.err.println("Caught exception: " + e.toString()); System.err.println(e.getStackTrace()); } finally { if (rIn != null) { try { rIn.close(); } catch (Exception e) { } } } } ArrayList<ShapefilePoint> digitizedPoints = new ArrayList<>(); double previousX = -1; double previousY = -1; Object[] recData; boolean isFeatureOpen = false; double mValue = 0; double zValue = 0; public void setMValue(double mValue) { this.mValue = mValue; } public void setZValue(double zValue) { this.zValue = zValue; } public void openNewFeature() { isFeatureOpen = true; digitizedPoints.clear(); } public void openNewFeature(Object[] recordData) { isFeatureOpen = true; digitizedPoints.clear(); this.recData = recordData; } public void addNodeToNewFeature(double x, double y) throws Exception { try { //if (x != previousX && y != previousY) { if (shapeType.getDimension() == ShapeTypeDimension.XY) { digitizedPoints.add(new ShapefilePoint(x, y)); } else if (shapeType.getDimension() == ShapeTypeDimension.Z) { digitizedPoints.add(new ShapefilePoint(x, y, zValue, mValue)); } else if (shapeType.getDimension() == ShapeTypeDimension.M) { digitizedPoints.add(new ShapefilePoint(x, y, mValue)); } else { return; } if (shapeType.getBaseType() == ShapeType.POINT) { closeNewFeature(); } previousX = x; previousY = y; //} return; } catch (Exception e) { throw e; } } public void deleteLastNodeInFeature() { int numPoints = digitizedPoints.size(); if (numPoints > 0) { digitizedPoints.remove(numPoints - 1); } } private void closeNewFeature() throws Exception { try { if (digitizedPoints.isEmpty()) { return; } PointsList pl = new PointsList(digitizedPoints); pl.removeDuplicates(); int[] parts = {0}; double x, y, z, m; switch (shapeType) { case POINT: x = pl.getPoint(0).x; y = pl.getPoint(0).y; whitebox.geospatialfiles.shapefile.Point wbPoint = new whitebox.geospatialfiles.shapefile.Point(x, y); shapefile.addRecord(wbPoint, recData); break; case POINTZ: x = pl.getPoint(0).x; y = pl.getPoint(0).y; z = pl.getPoint(0).z; m = pl.getPoint(0).m; PointZ wbPointZ = new PointZ(x, y, z, m); shapefile.addRecord(wbPointZ, recData); break; case POINTM: x = pl.getPoint(0).x; y = pl.getPoint(0).y; m = pl.getPoint(0).m; PointM wbPointM = new PointM(x, y, m); shapefile.addRecord(wbPointM, recData); break; case MULTIPOINT: MultiPoint mp = new MultiPoint(pl.getPointsArray()); shapefile.addRecord(mp, recData); break; case MULTIPOINTZ: MultiPointZ mpZ = new MultiPointZ(pl.getPointsArray(), pl.getZArray(), pl.getMArray()); shapefile.addRecord(mpZ, recData); break; case MULTIPOINTM: MultiPointM mpM = new MultiPointM(pl.getPointsArray(), pl.getMArray()); shapefile.addRecord(mpM, recData); break; case POLYLINE: PolyLine polyline = new PolyLine(parts, pl.getPointsArray()); shapefile.addRecord(polyline, recData); case POLYLINEZ: PolyLineZ polylineZ = new PolyLineZ(parts, pl.getPointsArray(), pl.getZArray(), pl.getMArray()); shapefile.addRecord(polylineZ, recData); case POLYLINEM: PolyLineM polylineM = new PolyLineM(parts, pl.getPointsArray(), pl.getMArray()); shapefile.addRecord(polylineM, recData); case POLYGON: if (pl.getPoint(0) != pl.getPoint(pl.size() - 1)) { pl.addPoint(pl.getPoint(0).x, pl.getPoint(0).y); } // Non-hole polygons must be in a clockwise order try { if (!isPointsListClockwiseOrder(pl)) { // reverse the order pl.reverseOrder(); } } catch (Exception e) { } Polygon polygon = new Polygon(parts, pl.getPointsArray()); shapefile.addRecord(polygon, recData); case POLYGONZ: if (pl.getPoint(0) != pl.getPoint(pl.size() - 1)) { pl.addZPoint(pl.getPoint(0).x, pl.getPoint(0).y, pl.getPoint(0).z, pl.getPoint(0).m); } // Non-hole polygons must be in a clockwise order try { if (!isPointsListClockwiseOrder(pl)) { // reverse the order pl.reverseOrder(); } } catch (Exception e) { } PolygonZ polygonZ = new PolygonZ(parts, pl.getPointsArray(), pl.getZArray(), pl.getMArray()); shapefile.addRecord(polygonZ, recData); case POLYGONM: if (pl.getPoint(0) != pl.getPoint(pl.size() - 1)) { pl.addMPoint(pl.getPoint(0).x, pl.getPoint(0).y, pl.getPoint(0).m); } // Non-hole polygons must be in a clockwise order try { if (!isPointsListClockwiseOrder(pl)) { // reverse the order pl.reverseOrder(); } } catch (Exception e) { } PolygonM polygonM = new PolygonM(parts, pl.getPointsArray(), pl.getMArray()); shapefile.addRecord(polygonM, recData); } shapefile.write(); reloadShapefile(); // fullExtent = new BoundingBox(shapefile.getxMin(), shapefile.getyMin(), // shapefile.getxMax(), shapefile.getyMax()); // recs = shapefile.getRecordsInBoundingBox(currentExtent, 1); // colourData = null; //openNewFeature(); } catch (Exception e) { throw e; //System.out.println(e.getMessage()); } finally { isFeatureOpen = false; } } public void closeNewFeature(double x, double y) throws Exception { try { if (x != previousX && y != previousY) { digitizedPoints.add(new ShapefilePoint(x, y)); previousX = x; previousY = y; } closeNewFeature(); } catch (Exception e) { throw e; } } public void reloadShapefile() { fullExtent = new BoundingBox(shapefile.getxMin(), shapefile.getyMin(), shapefile.getxMax(), shapefile.getyMax()); recs = shapefile.getRecordsInBoundingBox(currentExtent, 1); colourData = null; selectedFeatures = new boolean[shapefile.getNumberOfRecords() + 1]; } private boolean isPointsListClockwiseOrder(PointsList pl) throws Exception { // Note: holes are polygons that have verticies in counter-clockwise order // This approach is based on the method described by Paul Bourke, March 1998 // http://paulbourke.net/geometry/clockwise/index.html //PointsList pl = new PointsList(digitizedPoints); double[][] points = pl.getPointsArray(); int stPoint, endPoint, numPointsInPart; double x0, y0, x1, y1, x2, y2; int n1 = 0, n2 = 0, n3 = 0; stPoint = 0; // remember, the last point in each part is the same as the first...it's not a legitemate point. endPoint = points.length - 2; numPointsInPart = endPoint - stPoint + 1; if (numPointsInPart < 3) { throw new Exception("Degenerative polygon; fewer than 3 nodes."); } // something's wrong! // first see if it is a convex or concave polygon // calculate the cross product for each adjacent edge. double[] crossproducts = new double[numPointsInPart]; for (int j = 0; j < numPointsInPart; j++) { n2 = stPoint + j; if (j == 0) { n1 = stPoint + numPointsInPart - 1; n3 = stPoint + j + 1; } else if (j == numPointsInPart - 1) { n1 = stPoint + j - 1; n3 = stPoint; } else { n1 = stPoint + j - 1; n3 = stPoint + j + 1; } x0 = points[n1][0]; y0 = points[n1][1]; x1 = points[n2][0]; y1 = points[n2][1]; x2 = points[n3][0]; y2 = points[n3][1]; crossproducts[j] = (x1 - x0) * (y2 - y1) - (y1 - y0) * (x2 - x1); } boolean testSign; if (crossproducts[0] >= 0) { testSign = true; // positive } else { testSign = false; // negative } boolean isConvex = true; for (int j = 1; j < numPointsInPart; j++) { if (crossproducts[j] >= 0 && !testSign) { isConvex = false; break; } else if (crossproducts[j] < 0 && testSign) { isConvex = false; break; } } // now see if it is clockwise or counter-clockwise if (isConvex) { if (testSign) { // positive means counter-clockwise return false; } else { return true; } } else { // calculate the polygon area. If it is positive is is in clockwise order, else counter-clockwise. double area2 = 0; for (int j = 0; j < numPointsInPart; j++) { n1 = stPoint + j; if (j < numPointsInPart - 1) { n2 = stPoint + j + 1; } else { n2 = stPoint; } x1 = points[n1][0]; y1 = points[n1][1]; x2 = points[n2][0]; y2 = points[n2][1]; area2 += (x1 * y2) - (x2 * y1); } area2 = area2 / 2.0; if (area2 < 0) { // a positive area indicates counter-clockwise order return true; } else { return false; } } } /** * Used to select a feature contained in the vector. * * @param recordNumber the base-1 record index number */ public void selectFeature(int recordNumber) { if (!selectedFeatures[recordNumber]) { selectedFeatures[recordNumber] = true; // add it from the list of selected features selectedFeatureNumbers.add(recordNumber); this.pcs.firePropertyChange("selectedFeatureNumber", -2, recordNumber); } } /** * Used to select a feature contained in the vector based on bounding box. * Selected features must be contained within the box. * * @param box BoundingBox used to select features */ public void selectFeaturesByBox(BoundingBox box) { switch (shapeType) { case POLYGON: case POLYGONZ: case POLYGONM: case POLYLINE: case POLYLINEZ: case POLYLINEM: for (ShapeFileRecord record : recs) { BoundingBox bb = record.getGeometry().getBox(); if (bb.within(box)) { setSelectedFeature(record.getRecordNumber()); } } break; case POINT: case POINTZ: case POINTM: double pointX, pointY; double[][] points; for (ShapeFileRecord record : recs) { points = record.getGeometry().getPoints(); pointX = points[0][0]; pointY = points[0][1]; if (box.isPointInBox(pointX, pointY)) { setSelectedFeature(record.getRecordNumber()); } } break; // have to add something here for multipoints. } } /** * Used to select a feature contained in the vector based on the coordinates * of a point. * * @param x x coordinate * @param y y coordinate * @return int of selected feature's record number */ public int selectFeatureByLocation(double x, double y) { double minDist = Double.POSITIVE_INFINITY; double dist, boxCentreX, boxCentreY; int newSelectedFeatureNum = -1; switch (shapeType) { case POLYGON: case POLYGONZ: case POLYGONM: case POLYLINE: case POLYLINEZ: case POLYLINEM: for (ShapeFileRecord record : recs) { BoundingBox bb = record.getGeometry().getBox(); if (bb.isPointInBox(x, y)) { boxCentreX = bb.getMinX() + (bb.getMaxX() - bb.getMinX()) / 2; boxCentreY = bb.getMinY() + (bb.getMaxY() - bb.getMinY()) / 2; dist = (boxCentreX - x) * (boxCentreX - x) + (boxCentreY - y) * (boxCentreY - y); if (dist < minDist) { minDist = dist; newSelectedFeatureNum = record.getRecordNumber(); } } } break; case POINT: case POINTZ: case POINTM: double pointX, pointY; double[][] points; for (ShapeFileRecord record : recs) { points = record.getGeometry().getPoints(); pointX = points[0][0]; pointY = points[0][1]; dist = (pointX - x) * (pointX - x) + (pointY - y) * (pointY - y); if (dist < minDist) { minDist = dist; newSelectedFeatureNum = record.getRecordNumber(); } } break; case MULTIPOINT: case MULTIPOINTZ: case MULTIPOINTM: double x2, y2; double[][] points2; for (ShapeFileRecord record : recs) { points2 = record.getGeometry().getPoints(); for (int i = 0; i < points2.length; i++) { x2 = points2[0][0]; y2 = points2[0][1]; dist = (x2 - x) * (x2 - x) + (y2 - y) * (y2 - y); if (dist < minDist) { minDist = dist; newSelectedFeatureNum = record.getRecordNumber(); } } } break; } if (newSelectedFeatureNum >= 0) { setSelectedFeature(newSelectedFeatureNum); } return newSelectedFeatureNum; } /** * Used to save the selected features into a new vector file. * * @param fileName String with the directory and file name of the new file */ public void saveSelectedFeatures(String fileName) { try { shapeType = shapefile.getShapeType(); int numRecs = shapefile.getNumberOfRecords(); AttributeTable table = shapefile.getAttributeTable(); ShapeFile output = new ShapeFile(fileName, shapeType, table.getAllFields()); for (int i = 1; i <= numRecs; i++) { if (selectedFeatures[i]) { ShapeFileRecord record = shapefile.getRecord(i - 1); Object[] fields = table.getRecord(i - 1); output.addRecord(record.getGeometry(), fields); } } output.write(); } catch (Exception e) { } } public boolean doesAttributeFileExist() { return shapefile.databaseFileExists; } @Override public boolean isVisibleInLegend() { return visibleInLegend; } @Override public void setVisibleInLegend(boolean value) { this.visibleInLegend = value; } public class LegendEntry { public Color legendColour; public String legendLabel; protected LegendEntry(String label, Color clr) { legendLabel = label; legendColour = clr; } public Color getLegendColour() { return legendColour; } public String getLegendLabel() { return legendLabel; } } }