mirror of https://github.com/octoleo/plantuml.git
376 lines
12 KiB
Java
376 lines
12 KiB
Java
package ext.plantuml.com.ctreber.acearth;
|
|
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.util.ArrayList;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Random;
|
|
|
|
import ext.plantuml.com.ctreber.acearth.gui.CanvasACearth;
|
|
import ext.plantuml.com.ctreber.acearth.plugins.Plugin;
|
|
import ext.plantuml.com.ctreber.acearth.plugins.markers.Marker;
|
|
import ext.plantuml.com.ctreber.acearth.plugins.markers.PluginMarkers;
|
|
import ext.plantuml.com.ctreber.acearth.projection.Projection;
|
|
import ext.plantuml.com.ctreber.acearth.projection.ProjectionCyl;
|
|
import ext.plantuml.com.ctreber.acearth.projection.ProjectionMerc;
|
|
import ext.plantuml.com.ctreber.acearth.projection.ProjectionOrtho;
|
|
import ext.plantuml.com.ctreber.acearth.renderer.Renderer;
|
|
import ext.plantuml.com.ctreber.acearth.renderer.RowTypeRendererScanBit;
|
|
import ext.plantuml.com.ctreber.acearth.renderer.RowTypeRendererScanDot;
|
|
import ext.plantuml.com.ctreber.acearth.scanbit.BitGeneratorMap;
|
|
import ext.plantuml.com.ctreber.acearth.scanbit.BitGeneratorMapDefault;
|
|
import ext.plantuml.com.ctreber.acearth.scanbit.BitGeneratorMapOrtho;
|
|
import ext.plantuml.com.ctreber.acearth.scandot.DotGeneratorLines;
|
|
import ext.plantuml.com.ctreber.acearth.scandot.DotGeneratorStars;
|
|
import ext.plantuml.com.ctreber.acearth.scandot.ScanDot;
|
|
import ext.plantuml.com.ctreber.acearth.scandot.ScanDotGenerator;
|
|
import ext.plantuml.com.ctreber.acearth.shader.Shader;
|
|
import ext.plantuml.com.ctreber.acearth.shader.ShaderDefault;
|
|
import ext.plantuml.com.ctreber.acearth.shader.ShaderFlat;
|
|
import ext.plantuml.com.ctreber.acearth.shader.ShaderOrtho;
|
|
import ext.plantuml.com.ctreber.acearth.util.Coordinate;
|
|
import ext.plantuml.com.ctreber.acearth.util.SunPositionCalculator;
|
|
import ext.plantuml.com.ctreber.acearth.util.Toolkit;
|
|
import ext.plantuml.com.ctreber.aclib.sort.CTSort;
|
|
import ext.plantuml.com.ctreber.aclib.sort.QuickSort;
|
|
|
|
/**
|
|
* <h1>AC.earth - XEarth for Java</h1>
|
|
*
|
|
* <p>
|
|
* The original XEarth was written by Kirk Johnson in July 1993 - thank you for
|
|
* writing this great little program and making it available for free!
|
|
*
|
|
* <p>
|
|
* I wanted to extend the program, but not in C. So I created this Java version,
|
|
* and found the process quite <strike>painfull</strike> interesting. The
|
|
* biggest effort went into resolving references between C files and
|
|
* eliminatiing pointers.
|
|
*
|
|
* <h1>License</h1>
|
|
*
|
|
* <p>
|
|
* AC.earth Copyright (c) 2002 Christian Treber, ct@ctreber.com
|
|
*
|
|
* <p>
|
|
* AC.earth is based on XEarth by Kirk Johnson
|
|
*
|
|
* <p>
|
|
* To comply with the XEarth license I include the following text:
|
|
*
|
|
* <pre>
|
|
* XEarth Copyright (C) 1989, 1990, 1993-1995, 1999 Kirk Lauritz Johnson
|
|
* Parts of the source code are:
|
|
* Copyright (C) 1989, 1990, 1991 by Jim Frost
|
|
* Copyright (C) 1992 by Jamie Zawinski <jwz@lucid.com>
|
|
* Permission to use, copy, modify and freely distribute xearth for
|
|
* non-commercial and not-for-profit purposes is hereby granted
|
|
* without fee, provided that both the above copyright notice and this
|
|
* permission notice appear in all copies and in supporting
|
|
* documentation.
|
|
* [Section refering to GIF omitted because it doesn't apply to this version]
|
|
* The author makes no representations about the suitability of this
|
|
* software for any purpose. It is provided "as is" without express or
|
|
* implied warranty.
|
|
* THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT
|
|
* OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
|
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
* </pre>
|
|
*
|
|
* <p>
|
|
* The license for this program (AC.earth) is the same as the quoted license
|
|
* above, with one change: The "copyright notice and permission notice" shall
|
|
* include the entire text of this section.
|
|
*
|
|
* todo Phase 2: Make grid value stuff more meaningful ("every n degrees") todo
|
|
* Phase 2: Enter fixed time as data and time, not seconds since epoch todo
|
|
* Phase 2: Compact map data into binary file
|
|
*
|
|
* <p>
|
|
* © 2002 Christian Treber, ct@ctreber.com
|
|
*
|
|
* @author Christian Treber, ct@ctreber.com
|
|
*/
|
|
public class ACearth {
|
|
// :: remove folder when WASM
|
|
public static final String VERSION = "1.1";
|
|
public static final String BUILD = "22.11.2002 004";
|
|
|
|
// private static long fsStartTime = 0;
|
|
|
|
private ConfigurationACearth fConf = new ConfigurationACearth();
|
|
private long fCurrentTime;
|
|
|
|
private CanvasACearth fCanvas;
|
|
|
|
private Coordinate fViewPos;
|
|
private double fViewRotation;
|
|
|
|
private List fPlugins;
|
|
|
|
/**
|
|
* <p>
|
|
* Well, the main class.
|
|
*
|
|
* @param markers
|
|
*/
|
|
public ACearth(List<Marker> markers) {
|
|
// fsStartTime = System.currentTimeMillis();
|
|
|
|
fPlugins = new ArrayList();
|
|
fPlugins.add(new PluginMarkers(markers));
|
|
|
|
}
|
|
|
|
public void exportPng(OutputStream os) throws IOException {
|
|
fCanvas = new CanvasACearth(this, fConf.getInt("imageWidth"), fConf.getInt("imageHeight"));
|
|
update();
|
|
fCanvas.saveToImage(os);
|
|
}
|
|
|
|
public void update() throws IOException {
|
|
Projection lProjection = null;
|
|
Shader lShader = null;
|
|
BitGeneratorMap lScanner = null;
|
|
if (fConf.is("projection", "Cylindrical")) {
|
|
lProjection = new ProjectionCyl();
|
|
lScanner = new BitGeneratorMapDefault(lProjection);
|
|
lShader = new ShaderDefault();
|
|
}
|
|
|
|
if (fConf.is("projection", "Mercator")) {
|
|
lProjection = new ProjectionMerc();
|
|
lScanner = new BitGeneratorMapDefault(lProjection);
|
|
lShader = new ShaderDefault();
|
|
}
|
|
|
|
if (fConf.is("projection", "Orthographic")) {
|
|
lProjection = new ProjectionOrtho();
|
|
lScanner = new BitGeneratorMapOrtho(lProjection);
|
|
lShader = new ShaderOrtho();
|
|
}
|
|
|
|
computePositions();
|
|
lProjection.setImageWidth(fConf.getInt("imageWidth"));
|
|
lProjection.setImageHeight(fConf.getInt("imageHeight"));
|
|
lProjection.setShiftX(fConf.getInt("shiftX"));
|
|
lProjection.setShiftY(fConf.getInt("shiftY"));
|
|
lProjection.setViewMagnification(fConf.getDouble("viewMagnification"));
|
|
lProjection.setViewPos(fViewPos);
|
|
lProjection.setViewRotation(fViewRotation);
|
|
|
|
lScanner.setImageWidth(fConf.getInt("imageWidth"));
|
|
lScanner.setImageHeight(fConf.getInt("imageHeight"));
|
|
lScanner.setMapData(MapDataReader.readMapData());
|
|
// Process the map (produces ScanBit-s).
|
|
lScanner.generateScanBits();
|
|
|
|
// Process stars and lines (produces ScanDots-s).
|
|
List lScanDots = new ArrayList();
|
|
if (fConf.getBoolean("starsP")) {
|
|
ScanDotGenerator lGenerator = new DotGeneratorStars(fConf.getInt("imageWidth"), fConf.getInt("imageHeight"),
|
|
fConf.getDouble("starFrequency"), fConf.getInt("bigStars"), new Random(fCurrentTime));
|
|
lGenerator.generateScanDots();
|
|
lScanDots.addAll(lGenerator.getScanDots());
|
|
}
|
|
|
|
if (fConf.getBoolean("gridP")) {
|
|
ScanDotGenerator lGenerator = new DotGeneratorLines(lProjection, fConf.getInt("gridDivision"),
|
|
fConf.getInt("gridPixelDivision"));
|
|
lGenerator.generateScanDots();
|
|
lScanDots.addAll(lGenerator.getScanDots());
|
|
}
|
|
|
|
final CTSort lSort = new QuickSort();
|
|
ScanDot[] lScanDotsArray = (ScanDot[]) lScanDots.toArray(new ScanDot[0]);
|
|
lSort.sort(lScanDotsArray);
|
|
|
|
if (!fConf.getBoolean("shadeP")) {
|
|
lShader = new ShaderFlat();
|
|
}
|
|
lShader.setProjection(lProjection);
|
|
lShader.setSunPos(fConf.getSunPos());
|
|
lShader.setDaySideBrightness(fConf.getInt("daySideBrightness"));
|
|
lShader.setTerminatorDiscontinuity(fConf.getInt("terminatorDiscontinuity"));
|
|
lShader.setNightSideBrightness(fConf.getInt("nightSideBrightness"));
|
|
lShader.init();
|
|
|
|
Renderer lRenderer = new Renderer(fCanvas);
|
|
|
|
RowTypeRendererScanBit lRowRendererScanBit = new RowTypeRendererScanBit();
|
|
lRowRendererScanBit.setScanBits(lScanner.getScanBits());
|
|
lRenderer.addRowTypeRenderer(lRowRendererScanBit);
|
|
|
|
RowTypeRendererScanDot lRowRendererScanDot = new RowTypeRendererScanDot();
|
|
lRowRendererScanDot.setScanDots(lScanDotsArray);
|
|
lRenderer.addRowTypeRenderer(lRowRendererScanDot);
|
|
|
|
lRenderer.setShader(lShader);
|
|
lRenderer.render();
|
|
|
|
// Apply plugins
|
|
Iterator lIt = fPlugins.iterator();
|
|
while (lIt.hasNext()) {
|
|
Plugin lPlugin = (Plugin) lIt.next();
|
|
lPlugin.setProjection(lProjection);
|
|
lPlugin.setRenderTarget(fCanvas);
|
|
lPlugin.setParent(this);
|
|
lPlugin.render();
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* This is repeated when time changes since this influences the position of
|
|
* Earth.
|
|
*/
|
|
private void computePositions() {
|
|
// Determine time for rendering
|
|
if (fConf.getInt("fixedTime") == 0) {
|
|
// No fixed time.
|
|
// final long lTimePassed = System.currentTimeMillis() - fsStartTime;
|
|
// fCurrentTime = fsStartTime + (long) (fConf.getDouble("timeWarpFactor") *
|
|
// lTimePassed);
|
|
fCurrentTime = System.currentTimeMillis();
|
|
} else {
|
|
// Fixed time.
|
|
fCurrentTime = fConf.getInt("fixedTime") * 1000L;
|
|
}
|
|
|
|
if (fConf.getBoolean("sunMovesP")) {
|
|
fConf.setSunPos(SunPositionCalculator.getSunPositionOnEarth(fCurrentTime));
|
|
}
|
|
|
|
// Determine viewing position
|
|
if (fConf.is("viewPositionType", "Fixed")) {
|
|
fViewPos = fConf.getViewPos();
|
|
} else if (fConf.is("viewPositionType", "Sun-relative")) {
|
|
fViewPos = getSunRelativePosition();
|
|
} else if (fConf.is("viewPositionType", "Orbit")) {
|
|
fViewPos = getOrbitPosition(fCurrentTime);
|
|
} else if (fConf.is("viewPositionType", "Random")) {
|
|
fViewPos = getRandomPosition();
|
|
} else if (fConf.is("viewPositionType", "Moon")) {
|
|
fViewPos = SunPositionCalculator.getMoonPositionOnEarth(fCurrentTime);
|
|
}
|
|
|
|
// for ViewRotGalactic, compute appropriate viewing rotation
|
|
if (fConf.is("viewRotationType", "Galactic")) {
|
|
fViewRotation = (Toolkit.degsToRads(
|
|
fConf.getSunPos().getLat() * Math.sin((fViewPos.getLong() - fConf.getSunPos().getLong()))));
|
|
} else {
|
|
fViewRotation = fConf.getDouble("viewRotation");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* Add sun position and position relative to sun, straighten out the result.
|
|
*
|
|
* @return Position relativ to sun position as defined by fSunPosRel.
|
|
*/
|
|
private Coordinate getSunRelativePosition() {
|
|
final Coordinate lPos = fConf.getSunPos();
|
|
lPos.add(fConf.getSunPosRel());
|
|
|
|
return lPos;
|
|
}
|
|
|
|
private Coordinate getOrbitPosition(long pTimeMillis) {
|
|
double x, y, z;
|
|
double a, c, s;
|
|
double t1, t2;
|
|
|
|
/* start at 0 N 0 E */
|
|
x = 0;
|
|
y = 0;
|
|
z = 1;
|
|
|
|
/*
|
|
* rotate in about y axis (from z towards x) according to the number of orbits
|
|
* we've completed
|
|
*/
|
|
a = (double) pTimeMillis / (fConf.getDouble("orbitPeriod") * 3600 * 1000) * 2 * Math.PI;
|
|
c = Math.cos(a);
|
|
s = Math.sin(a);
|
|
t1 = c * z - s * x;
|
|
t2 = s * z + c * x;
|
|
z = t1;
|
|
x = t2;
|
|
|
|
/*
|
|
* rotate about z axis (from x towards y) according to the inclination of the
|
|
* orbit
|
|
*/
|
|
a = Toolkit.degsToRads(fConf.getDouble("orbitInclination"));
|
|
c = Math.cos(a);
|
|
s = Math.sin(a);
|
|
t1 = c * x - s * y;
|
|
t2 = s * x + c * y;
|
|
x = t1;
|
|
y = t2;
|
|
|
|
/*
|
|
* rotate about y axis (from x towards z) according to the number of rotations
|
|
* the earth has made
|
|
*/
|
|
a = ((double) pTimeMillis / 86400000) * (2 * Math.PI);
|
|
c = Math.cos(a);
|
|
s = Math.sin(a);
|
|
t1 = c * x - s * z;
|
|
t2 = s * x + c * z;
|
|
x = t1;
|
|
z = t2;
|
|
|
|
return new Coordinate(Toolkit.radsToDegs(Math.asin(y)), Toolkit.radsToDegs(Math.atan2(x, z)));
|
|
}
|
|
|
|
/**
|
|
* <p>
|
|
* Pick a position (lat, lon) at random
|
|
*
|
|
* @return A random position.
|
|
*/
|
|
private static Coordinate getRandomPosition() {
|
|
|
|
/* select a vector at random */
|
|
final double[] pos = new double[3];
|
|
double mag = 0;
|
|
do {
|
|
for (int i = 0; i < 3; i++) {
|
|
pos[i] = ((Math.random() * 20000) * 1e-4) - 1;
|
|
mag += pos[i] * pos[i];
|
|
}
|
|
} while ((mag > 1.0) || (mag < 0.01));
|
|
|
|
/* normalize the vector */
|
|
mag = Math.sqrt(mag);
|
|
for (int i = 0; i < 3; i++) {
|
|
pos[i] /= mag;
|
|
}
|
|
|
|
/* convert to (lat, lon) */
|
|
final double s_lat = pos[1];
|
|
final double c_lat = Math.sqrt(1 - s_lat * s_lat);
|
|
final double s_lon = pos[0] / c_lat;
|
|
final double c_lon = pos[2] / c_lat;
|
|
|
|
return new Coordinate(Math.atan2(s_lat, c_lat) * (180 / Math.PI), Math.atan2(s_lon, c_lon) * (180 / Math.PI));
|
|
}
|
|
|
|
// public static long getStartTime() {
|
|
// return fsStartTime;
|
|
// }
|
|
|
|
public ConfigurationACearth getConf() {
|
|
return fConf;
|
|
}
|
|
|
|
}
|