/* * 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.cartographic; import java.awt.Color; import java.awt.Font; import java.text.DecimalFormat; import whitebox.interfaces.CartographicElement; /** * * @author johnlindsay */ public class MapScale implements CartographicElement, Comparable<CartographicElement> { double pointsPerMetre = java.awt.Toolkit.getDefaultToolkit().getScreenResolution() * 39.3701; boolean visible = true; boolean selected = false; int number = -1; boolean showRepresentativeFraction = false; boolean showGraphicalScale = true; boolean borderVisible = false; boolean backgroundVisible = false; int upperLeftX = -32768; int upperLeftY = -32768; int height = 50; // in points int width = 150; // in points int margin = 10; double barLength = 5.0; int numberDivisions = 5; String units = "metres"; double conversionToMetres = 1; Color backColour = Color.WHITE; Color borderColour = Color.BLACK; Color outlineColour = Color.BLACK; Color legendColour = Color.BLACK; Color fontColour = Color.BLACK; static DecimalFormat dfScale = new DecimalFormat("###,###,###.#"); String representativeFraction; String lowerLabel = "0"; String upperLabel = "5"; String name = "mapScale"; float lineWidth = 0.75f; private MapArea mapArea = null; private int selectedOffsetX; private int selectedOffsetY; boolean outlineVisible = false; Font labelFont = new Font("SanSerif", Font.PLAIN, 10); private ScaleStyle scaleStyle = ScaleStyle.STANDARD; public enum ScaleStyle { STANDARD, SIMPLE, COMPLEX, COMPACT; } public MapScale(String name) { this.name = name; } public boolean isRepresentativeFractionVisible() { return showRepresentativeFraction; } public void setRepresentativeFractionVisible(boolean showRepresentativeFraction) { this.showRepresentativeFraction = showRepresentativeFraction; if (!showRepresentativeFraction && !showGraphicalScale) { showGraphicalScale = true; } } public boolean isGraphicalScaleVisible() { return showGraphicalScale; } public void setGraphicalScaleVisible(boolean showGraphicalScale) { this.showGraphicalScale = showGraphicalScale; if (!showRepresentativeFraction && !showGraphicalScale) { showRepresentativeFraction = true; } } public String getUnits() { return units; } public void setUnits(String units) { if (mapArea != null && (mapArea.getXYUnits().toLowerCase().contains("deg") || mapArea.getXYUnits().contains("\u00B0"))) { units = "degrees"; } this.units = units; if (units.toLowerCase().contains("met")) { conversionToMetres = 1.0; } else if (units.toLowerCase().equals("m")) { conversionToMetres = 1.0; } else if (units.toLowerCase().contains("feet")) { conversionToMetres = 0.3048; } else if (units.toLowerCase().contains("ft")) { conversionToMetres = 0.3048; } else if (units.toLowerCase().contains("miles")) { conversionToMetres = 1609.34; } else if (units.toLowerCase().contains("mi")) { conversionToMetres = 1609.34; } else if (units.toLowerCase().contains("kilo")) { conversionToMetres = 1000; } else if (units.toLowerCase().contains("km")) { conversionToMetres = 1000; } else if (units.toLowerCase().contains("deg")) { double p1 = 111412.84; // longitude calculation term 1 double p2 = -93.5; // longitude calculation term 2 double p3 = 0.118; // longitude calculation term 3 double lat = 45.0; if (mapArea != null) { lat = (mapArea.getCurrentMapExtent().getMaxY() + mapArea.getCurrentMapExtent().getMinY()) / 2.0; } lat = Math.toRadians(lat); double longlen = (p1 * Math.cos(lat)) + (p2 * Math.cos(3 * lat)) + (p3 * Math.cos(5 * lat)); conversionToMetres = 1.0 / (longlen / 1000.0); } else { this.units = "metres"; conversionToMetres = 1.0; } } @Override public int getUpperLeftX() { return upperLeftX; } @Override public void setUpperLeftX(int upperLeftX) { this.upperLeftX = upperLeftX; } @Override public int getUpperLeftY() { return upperLeftY; } @Override public void setUpperLeftY(int upperLeftY) { this.upperLeftY = upperLeftY; } @Override public int getLowerRightX() { return upperLeftX + width; } @Override public int getLowerRightY() { return upperLeftY + height; } @Override public int getSelectedOffsetX() { return selectedOffsetX; } @Override public void setSelectedOffsetX(int selectedOffsetX) { this.selectedOffsetX = selectedOffsetX; } @Override public int getSelectedOffsetY() { return selectedOffsetY; } @Override public void setSelectedOffsetY(int selectedOffsetY) { this.selectedOffsetY = selectedOffsetY; } @Override public boolean isVisible() { return visible; } @Override public void setVisible(boolean visible) { this.visible = visible; } public ScaleStyle getScaleStyle() { return scaleStyle; } public void setScaleStyle(ScaleStyle scaleStyle) { this.scaleStyle = scaleStyle; } int mapAreaElementNumber = -1; public int getMapAreaElementNumber() { if (mapAreaElementNumber < 0) { mapAreaElementNumber = mapArea.getElementNumber(); } return mapAreaElementNumber; } public void setMapAreaElementNumber(int num) { this.mapAreaElementNumber = num; } public boolean isOutlineVisible() { return outlineVisible; } public void setOutlineVisible(boolean outlineVisible) { this.outlineVisible = outlineVisible; } public double getBarLength() { return barLength; } // public void setBarLength(double barLength) { // this.barLength = barLength; // } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public int getNumberDivisions() { return numberDivisions; } public void setNumberDivisions(int numberDivisions) { this.numberDivisions = numberDivisions; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public Color getBackColour() { return backColour; } public void setBackColour(Color backColour) { this.backColour = backColour; } public Color getBorderColour() { return borderColour; } public void setBorderColour(Color outlineColour) { this.borderColour = outlineColour; } public Color getLegendColour() { return legendColour; } public void setLegendColour(Color legendColour) { this.legendColour = legendColour; } public Color getFontColour() { return fontColour; } public void setFontColour(Color fontColour) { this.fontColour = fontColour; } public boolean isBorderVisible() { return borderVisible; } public void setBorderVisible(boolean borderVisible) { this.borderVisible = borderVisible; } public Font getLabelFont() { return labelFont; } public void setLabelFont(Font labelFont) { this.labelFont = labelFont; } public double getScale() { return scale; // if (mapArea != null) { // double scale = mapArea.getScale(); // //what is the width of the scale box in ground units? // double widthGU = (width - 4 * margin) / pointsPerMetre * scale / conversionToMetres; // // the number of divisions can range between 2 and 10 // // given this, figure out what the appropriate division length is // double[] possibleLengths = {0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 50.0, 100.0, 500.0, 1000.0, 5000.0, 10000.0, 50000.0, 100000.0}; // int[] numDecimals = {3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // int closestDivision = 0; // double closestDivisionBarLength = 0; // double minDist = Float.POSITIVE_INFINITY; // double dist; // int numDecimalsInLabel = 0; // for (int a = 2; a <= 10; a++) { // for (int b = 0; b < possibleLengths.length; b++) { // dist = Math.abs(widthGU - (a * possibleLengths[b])); // if ((dist < minDist) && (a * possibleLengths[b] < widthGU)) { // minDist = dist; // closestDivision = a; // closestDivisionBarLength = possibleLengths[b]; // numDecimalsInLabel = numDecimals[b]; // } // } // } // barLength = closestDivisionBarLength * closestDivision; // numberDivisions = closestDivision; // // // what are the upper and lower labels? // String formatString = "0"; // if (numDecimalsInLabel > 0) { // formatString += "."; // for (int a = 0; a < numDecimalsInLabel; a++) { // formatString += "0"; // } // } // DecimalFormat df = new DecimalFormat(formatString); // lowerLabel = df.format(0.0); // upperLabel = df.format(barLength); // // // return scale; // } else { // return 0.0; // } } private double scale = 0; public void setScale() { representativeFraction = "Scale 1:" + dfScale.format(scale); this.scale = mapArea.getScale(); //what is the width of the scale box in ground units? double widthGU = (width - 4 * margin) / pointsPerMetre * scale / conversionToMetres; // the number of divisions can range between 2 and 10 // given this, figure out what the appropriate division length is // double[] possibleLengths = {0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 50.0, 100.0, 500.0, 1000.0, 5000.0, 10000.0, 50000.0, 100000.0}; // int[] numDecimals = {3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; double[] possibleLengths = {0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 100.0, 500.0, 1000.0, 5000.0, 10000.0, 50000.0, 100000.0}; int[] numDecimals = {3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int closestDivision = 0; double closestDivisionBarLength = 0; double minDist = Float.POSITIVE_INFINITY; double dist; int numDecimalsInLabel = 0; for (int a = 2; a <= 10; a++) { for (int b = 0; b < possibleLengths.length; b++) { dist = Math.abs(widthGU - (a * possibleLengths[b])); if ((dist < minDist) && (a * possibleLengths[b] < widthGU)) { minDist = dist; closestDivision = a; closestDivisionBarLength = possibleLengths[b]; numDecimalsInLabel = numDecimals[b]; } } } barLength = closestDivisionBarLength * closestDivision; numberDivisions = closestDivision; // what are the upper and lower labels? String formatString = "0"; if (numDecimalsInLabel > 0) { formatString += "."; for (int a = 0; a < numDecimalsInLabel; a++) { formatString += "0"; } } DecimalFormat df = new DecimalFormat(formatString); lowerLabel = df.format(0.0); upperLabel = df.format(barLength); } public String getRepresentativeFraction() { return representativeFraction; } public int getMargin() { return margin; } public void setMargin(int scaleMargin) { this.margin = scaleMargin; } public double getConversionToMetres() { return conversionToMetres; } public void setConversionToMetres(double conversionToMetres) { this.conversionToMetres = conversionToMetres; } public String getLowerLabel() { return lowerLabel; } public void setLowerLabel(String lowerLabel) { this.lowerLabel = lowerLabel; } public String getUpperLabel() { return upperLabel; } public void setUpperLabel(String upperLabel) { this.upperLabel = upperLabel; } public boolean isBackgroundVisible() { return backgroundVisible; } public void setBackgroundVisible(boolean backgroundVisible) { this.backgroundVisible = backgroundVisible; } @Override public boolean isSelected() { return selected; } @Override public void setSelected(boolean selected) { this.selected = selected; } public Color getOutlineColour() { return outlineColour; } public void setOutlineColour(Color outlineColour) { this.outlineColour = outlineColour; } @Override public int getElementNumber() { return number; } @Override public void setElementNumber(int number) { this.number = number; } @Override public String getName() { return name; } @Override public void setName(String name) { this.name = name; } public float getLineWidth() { return lineWidth; } public void setLineWidth(float lineWidth) { this.lineWidth = lineWidth; } public MapArea getMapArea() { return mapArea; } public void setMapArea(MapArea mapArea) { this.mapArea = mapArea; getScale(); } @Override public int compareTo(CartographicElement other) { final int BEFORE = -1; final int EQUAL = 0; final int AFTER = 1; // compare them based on their element (overlay) numbers if (this.number < other.getElementNumber()) { return BEFORE; } else if (this.number > other.getElementNumber()) { return AFTER; } return EQUAL; } @Override public void resize(int x, int y, int resizeMode) { int minSizeX = 30; int minSizeY = 30; int deltaX = 0; int deltaY = 0; switch (resizeMode) { case 0: // off the north edge deltaY = y - upperLeftY; if (height - deltaY >= minSizeY) { upperLeftY = y; height -= deltaY; } break; case 1: // off the south edge deltaY = y - (upperLeftY + height); if (height + deltaY >= minSizeY) { height += deltaY; } break; case 2: // off the east edge deltaX = x - (upperLeftX + width); if (width + deltaX >= minSizeX) { width += deltaX; } break; case 3: // off the west edge deltaX = x - upperLeftX; if (width - deltaX >= minSizeX) { upperLeftX = x; width -= deltaX; } break; case 4: // off the northeast edge deltaY = y - upperLeftY; if (height - deltaY >= minSizeY) { upperLeftY = y; height -= deltaY; } deltaX = x - (upperLeftX + width); if (width + deltaX >= minSizeX) { width += deltaX; } break; case 5: // off the northwest edge deltaY = y - upperLeftY; if (height - deltaY >= minSizeY) { upperLeftY = y; height -= deltaY; } deltaX = x - upperLeftX; if (width - deltaX >= minSizeX) { upperLeftX = x; width -= deltaX; } break; case 6: // off the southeast edge deltaY = y - (upperLeftY + height); if (height + deltaY >= minSizeY) { height += deltaY; } deltaX = x - (upperLeftX + width); if (width + deltaX >= minSizeX) { width += deltaX; } break; case 7: // off the southwest edge deltaY = y - (upperLeftY + height); if (height + deltaY >= minSizeY) { height += deltaY; } deltaX = x - upperLeftX; if (width - deltaX >= minSizeX) { upperLeftX = x; width -= deltaX; } break; } } @Override public CartographicElementType getCartographicElementType() { return CartographicElementType.MAP_SCALE; } }