/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2014-2016, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.renderer.windbarbs;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geotools.util.Utilities;
/**
* A WindBarb object made of reference speed in knots, and related number of longBarbs (10 kts), shortBarbs (5 kts) and pennants (50 kts).
*
* @author Daniele Romagnoli, GeoSolutions SAS
*/
class WindBarb {
/** The logger. */
private static final Logger LOGGER = org.geotools.util.logging.Logging
.getLogger(WindBarb.class);
/**
* A WindBarbDefinition contains parameters used to build the WindBarb, such as the main vector length, the elements Spacing, the length of long
* barbs...
*
* @author Daniele Romagnoli, GeoSolutions SAS
*
*/
static class WindBarbDefinition {
public WindBarbDefinition(final int vectorLength, final int basePennantLength,
final int elementsSpacing, final int longBarbLength, final int zeroWindRadius) {
// checks
if (vectorLength <= 0) {
throw new IllegalArgumentException("Invalid vectorLength:" + vectorLength);
}
if (basePennantLength <= 0) {
throw new IllegalArgumentException("Invalid basePennantLength:" + vectorLength);
}
if (elementsSpacing <= 0) {
throw new IllegalArgumentException("Invalid elementsSpacing:" + vectorLength);
}
if (vectorLength < elementsSpacing + basePennantLength) {
throw new IllegalArgumentException(
"Invalid vectorLength<elementsSpacing+basePennantLength : " + vectorLength);
}
if (longBarbLength <= 0) {
throw new IllegalArgumentException("Invalid longBarbLength:" + vectorLength);
}
if (zeroWindRadius <= 0) {
throw new IllegalArgumentException("Invalid zeroWindRadius:" + vectorLength);
}
// code
this.vectorLength = vectorLength;
this.basePennantLength = basePennantLength;
this.elementsSpacing = elementsSpacing;
this.longBarbLength = longBarbLength;
this.shortBarbLength = longBarbLength / 2;
this.zeroWindRadius = zeroWindRadius;
}
/** The main vector length */
int vectorLength;
/** The length of the base of the pennant (the triangle) */
int basePennantLength;
/** The distance between multiple barbs, and pennants */
int elementsSpacing;
/** The length of a long barb */
int longBarbLength;
/** The length of a short barb (is always half the length of a long barb) */
int shortBarbLength;
int zeroWindRadius;
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("WindBarbDefinition [vectorLength=");
builder.append(vectorLength);
builder.append(", basePennantLength=");
builder.append(basePennantLength);
builder.append(", elementsSpacing=");
builder.append(elementsSpacing);
builder.append(", longBarbLength=");
builder.append(longBarbLength);
builder.append(", shortBarbLength=");
builder.append(shortBarbLength);
builder.append(", zeroWindRadius=");
builder.append(zeroWindRadius);
builder.append("]");
return builder.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + basePennantLength;
result = prime * result + elementsSpacing;
result = prime * result + longBarbLength;
result = prime * result + shortBarbLength;
result = prime * result + vectorLength;
result = prime * result + zeroWindRadius;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof WindBarbDefinition)) {
return false;
}
WindBarbDefinition other = (WindBarbDefinition) obj;
if (basePennantLength != other.basePennantLength) {
return false;
}
if (elementsSpacing != other.elementsSpacing) {
return false;
}
if (longBarbLength != other.longBarbLength) {
return false;
}
if (shortBarbLength != other.shortBarbLength) {
return false;
}
if (vectorLength != other.vectorLength) {
return false;
}
if (zeroWindRadius != other.zeroWindRadius) {
return false;
}
return true;
}
}
int knots;
int pennants;
int longBarbs;
int shortBarbs;
static int DEFAULT_BARB_LENGTH = 20;
static int DEFAULT_BASE_PENNANT_LENGTH = 5;
static int DEFAULT_ELEMENTS_SPACING = 5;
static int DEFAULT_FLAGPOLE_LENGTH = 40;
static int DEFAULT_ZERO_WIND_RADIUS = 5;
/** A {@link WindBarbDefinition} instance reporting structural values for a WindBarb (vector length, sizes, ...) */
WindBarbDefinition windBarbDefinition;
static WindBarbDefinition DEFAULT_WINDBARB_DEFINITION = new WindBarbDefinition(
WindBarb.DEFAULT_FLAGPOLE_LENGTH, WindBarb.DEFAULT_BASE_PENNANT_LENGTH,
WindBarb.DEFAULT_ELEMENTS_SPACING, WindBarb.DEFAULT_BARB_LENGTH,
WindBarb.DEFAULT_ZERO_WIND_RADIUS);
public WindBarb(final int knots) {
this(DEFAULT_WINDBARB_DEFINITION, knots);
}
public WindBarb(final WindBarbDefinition definition, final int knots) {
Utilities.ensureNonNull("definition", definition);
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("Creating WindBarb for knots: " + knots + " and WindBarbDefinition:"
+ definition.toString());
}
this.windBarbDefinition = definition;
this.knots = knots;
if (knots == -1) {
return;
} else {
if (knots < 0) {
throw new IllegalArgumentException("Illegal wind speeds(kn): " + knots);
}
if (knots > WindBarbsFactory.MAX_SPEED) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.fine("speed is exceeding MaxSpeed = " + WindBarbsFactory.MAX_SPEED + "."
+ "\nThe related WindBarb isn't in the cache") ;
}
}
}
pennants = knots / 50;
longBarbs = (knots - (pennants * 50)) / 10;
shortBarbs = (knots -(pennants * 50) - (longBarbs * 10)) / 5;
}
/**
* Build a {@Shape} WindBarb
*
* @return
*/
Shape build() {
if (knots < 0) {
return buildAbsentModule();
} else {
return buildStandardBarb();
}
}
/**
* @return
*
*/
private Shape buildStandardBarb() {
int positionOnPath = -windBarbDefinition.vectorLength;
// Base barb
Path2D path = new Path2D.Double();
// Initialize Barb
if (knots < 5) {
// let's use a circle for Calm
return new Ellipse2D.Float(-windBarbDefinition.zeroWindRadius / 2.0f, // the X coordinate of the upper-left corner of the specified
// rectangular area
-windBarbDefinition.zeroWindRadius / 2.0f, // the Y coordinate of the upper-left corner of the specified rectangular area
windBarbDefinition.zeroWindRadius, windBarbDefinition.zeroWindRadius);
} else {
// draw wind barb line
path.moveTo(0, 0);
path.lineTo(0, positionOnPath);
}
// pennants management
if (pennants > 0) {
positionOnPath = drawPennants(path, positionOnPath);
positionOnPath += windBarbDefinition.elementsSpacing; // add spacing
}
// long barbs management
if (longBarbs > 0) {
positionOnPath = drawLongBarbs(path, positionOnPath);
positionOnPath += windBarbDefinition.elementsSpacing; // add spacing
}
// short barbs management
if (shortBarbs > 0) {
positionOnPath = drawShortBarb(path, positionOnPath);
}
// FLIP for geotools
final Shape createTransformedShape = path.createTransformedShape(AffineTransform
.getScaleInstance(1, -1));
return createTransformedShape;
}
/**
* Build a {@Shape} WindBarb
*
* @return
*/
Shape buildAbsentModule() {
// Base barb
Path2D path = new Path2D.Double();
int positionOnPath = 0;
// draw wind barb line
path.moveTo(0, positionOnPath);
positionOnPath -= windBarbDefinition.vectorLength;
path.lineTo(0, positionOnPath);
path.moveTo(windBarbDefinition.shortBarbLength / 2.0f, positionOnPath
- windBarbDefinition.shortBarbLength / 2.0f);
path.lineTo(-windBarbDefinition.shortBarbLength / 2.0f, positionOnPath
+ windBarbDefinition.shortBarbLength / 2.0f);
path.moveTo(-windBarbDefinition.shortBarbLength / 2.0f, positionOnPath
- windBarbDefinition.shortBarbLength / 2.0f);
path.lineTo(windBarbDefinition.shortBarbLength / 2.0f, positionOnPath
+ windBarbDefinition.shortBarbLength / 2.0f);
// FLIP for geotools
final Shape createTransformedShape = path.createTransformedShape(AffineTransform
.getScaleInstance(1, -1));
return createTransformedShape;
}
/**
* Add short barbs to the shape
*
* @param path
* @param positionOnPath
* @return
*/
private int drawShortBarb(Path2D path, int positionOnPath) {
if (pennants == 0 && longBarbs == 0) {
positionOnPath = -windBarbDefinition.vectorLength + DEFAULT_ELEMENTS_SPACING;
}
path.moveTo(0, positionOnPath);
path.lineTo(windBarbDefinition.shortBarbLength, positionOnPath
- windBarbDefinition.basePennantLength / 4.0);
return positionOnPath;
}
/**
* Add long barbs to the shape
*
* @param path
* @param positionOnPath
* @return
*/
private int drawLongBarbs(Path2D path, int positionOnPath) {
if (longBarbs <= 0) {
return positionOnPath;
}
for (int elements = 0; elements < longBarbs; elements++) {
if (elements >= 1) {
// spacing if neede
positionOnPath += windBarbDefinition.elementsSpacing;
}
// draw long barb
path.moveTo(0, positionOnPath);
path.lineTo(windBarbDefinition.longBarbLength, positionOnPath
- windBarbDefinition.basePennantLength / 2.0);
}
return positionOnPath;
}
/**
* add Pennants to the shape
*
* @param path
* @param positionOnPath
* @return
*/
private int drawPennants(Path2D path, int positionOnPath) {
if (pennants <= 0) {
return positionOnPath;
}
for (int elements = 0; elements < pennants; elements++) {
// move forward one pennant at a time
// draw pennant
path.moveTo(0, positionOnPath);
positionOnPath += windBarbDefinition.basePennantLength / 2.0;
path.lineTo(windBarbDefinition.longBarbLength, positionOnPath); // first edge
positionOnPath += windBarbDefinition.basePennantLength / 2.0;
path.lineTo(0, positionOnPath); // second edge
path.closePath();
// only one square
}
return positionOnPath;
}
}