package net.sourceforge.gpstools;
/* gpsdings
* Copyright (C) 2006 Moritz Ringler
* $Id: AbstractMapMaker.java 441 2010-12-13 20:04:20Z ringler $
*
* 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/>.
*/
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import net.sourceforge.gpstools.gpx.Gpx;
import net.sourceforge.gpstools.gpx.GpxType;
import net.sourceforge.gpstools.gpx.Trk;
import net.sourceforge.gpstools.gpx.Trkpt;
import net.sourceforge.gpstools.gpx.Trkseg;
import net.sourceforge.gpstools.gpx.Wpt;
import net.sourceforge.gpstools.utils.ColorListColorProvider;
import net.sourceforge.gpstools.utils.ColorProvider;
import net.sourceforge.gpstools.utils.TemplateEvent;
import net.sourceforge.gpstools.utils.TemplateHandler;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
public abstract class AbstractMapMaker extends GPSDings implements
TemplateHandler {
public final static float DEFAULT_PADDING = 0.1f;
protected final Map<Output, String> url = new EnumMap<Output, String>(
Output.class);
private String apiKey = "not specified";
private String title = "GPSDings Map";
String photojs = null;
private String imageURLPrefix = "";
private GpxType data = new Gpx();
private Rectangle2D.Double bounds = null;
private final Object sync = new Object();
private float padding = DEFAULT_PADDING;
private boolean includeTrks = true;
private boolean includeWpts = true;
public AbstractMapMaker() {
url.put(Output.CSS, "map.css");
url.put(Output.MapJS, "map.js");
}
public void setIncludeTrks(boolean b) {
includeTrks = b;
}
public boolean getIncludeTrks() {
return includeTrks;
}
public void setIncludeWpts(boolean b) {
includeWpts = b;
}
public boolean getIncludeWpts() {
return includeWpts;
}
public void setPadding(float padding) {
if (padding < 0) {
throw new IllegalArgumentException("Padding must be >= 0.");
}
this.padding = padding;
}
public float getPadding() {
return this.padding;
}
public void addGPX(GpxType gpx) {
synchronized (sync) {
merge(data, gpx);
bounds = null;
}
}
public void addGpx(InputStream ins) throws IOException {
addGPX(GPSDings.readGPX(ins));
}
public void setGPX(GpxType gpx) {
synchronized (sync) {
data = gpx;
bounds = null;
}
}
protected GpxType getGpx() {
return data;
}
private static GpxType merge(GpxType dst, GpxType src) {
for (Trk trk : src.getTrk()) {
dst.addTrk(trk);
}
for (Wpt wpt : src.getWpt()) {
dst.addWpt(wpt);
}
return dst;
}
public void setGMapApiKey(String gMapApiKey) {
apiKey = gMapApiKey;
}
String getGMapApiKey() {
return apiKey;
}
public void setURL(Output o, String url) {
this.url.put(o, url);
}
public String getURL(Output o) {
return url.get(o);
}
public void setHTMLTitle(String title) {
this.title = title;
}
public String getHTMLTitle() {
return title;
}
public void setPhotoJavascript(String url) {
photojs = url;
}
public String getPhotoJavaScript() {
return photojs;
}
public void setPhotoURLPrefix(String urlprefix) {
imageURLPrefix = urlprefix;
}
public String getPhotoURLPrefix() {
return imageURLPrefix;
}
@Override
public void print(Output out) throws IOException {
File f = output.get(out);
OutputStream os = (f == null) ? new BufferedOutputStream(System.out)
: new BufferedOutputStream(new FileOutputStream(f));
try {
switch (out) {
case HTML:
printHTML(os);
break;
case CSS:
System.err.println("WARNING: CSS output no longer supported.");
break;
case MapJS:
printJS(os);
break;
default:
System.err.println("Unsupported output type " + out);
}
} finally {
if (os != System.out) {
os.close();
}
}
}
protected abstract void printHTML(OutputStream out) throws IOException;
protected abstract void printJS(OutputStream out) throws IOException;
protected abstract CharSequence draw(Trkpt[] pts, String color, String name);
protected void drawTracks(Appendable mapjs) throws IOException {
ColorProvider colorGen = new ColorListColorProvider("#RGB");// new
// RandomColorProvider("#RGB");//
for (Trk trk : data.getTrk()) {
final String trkname = trk.getName();
message(trkname);
Trkseg[] segs = trk.getTrkseg();
final int segCount = segs.length;
for (int iseg = 0; iseg < segCount; iseg++) {
Trkseg seg = segs[iseg];
if (seg.getTrkptCount() < 2) {
continue;
}
/*
* if at least one trkpt has a timestamp we accept only trkpts
* with timestamps
*/
Trkpt[] pts = seg.getTrkpt();
List<Trkpt> withTimeStamp = new Vector<Trkpt>(pts.length);
for (Trkpt pt : pts) {
if (pt.getTime() != null) {
withTimeStamp.add(pt);
}
}
final int nTimestampPts = withTimeStamp.size();
if (nTimestampPts == 1) {
message("Dropping segment with exactly one point with a timestamp.");
continue;
} else if (nTimestampPts != 0) {
if (nTimestampPts != pts.length) {
message("Dropping " + (pts.length - nTimestampPts)
+ " points with missing timestamps.");
}
pts = withTimeStamp
.toArray(new Trkpt[withTimeStamp.size()]);
}
String segname = null;
if (trkname != null) {
segname = trkname;
if (segCount > 1) {
segname = segname + "[" + iseg + "]";
}
}
mapjs.append("//Track: ").append(String.valueOf(segname));
mapjs.append("\n");
mapjs.append(draw(pts, colorGen.getNextColorString(), segname));
}
}
}
protected Rectangle2D.Double getBounds() {
synchronized (sync) {
if (bounds == null) {
Point2D.Double point = null;
for (Wpt pt : data.getWpt()) {
point = new Point2D.Double(pt.getLat().doubleValue(), pt
.getLon().doubleValue());
if (bounds == null) {
bounds = new Rectangle2D.Double(point.x, point.y, 0, 0);
} else {
bounds.add(point);
}
}
for (Trk trk : data.getTrk()) {
for (Trkseg seg : trk.getTrkseg()) {
for (Trkpt pt : seg.getTrkpt()) {
point = new Point2D.Double(pt.getLat()
.doubleValue(), pt.getLon().doubleValue());
if (bounds == null) {
bounds = new Rectangle2D.Double(point.x,
point.y, 0, 0);
} else {
bounds.add(point);
}
}
}
}
}
}
return bounds;
}
protected void includeGPSDingsJS(Appendable html) throws IOException {
html.append("<script type=\"text/javascript\" src=\"http://gpstools.sf.net/js/GPSDings-");
html.append(GPSDings.jsVersion());
html.append(".js\"></script>\n");
}
protected void includePhotoJS(Appendable html) throws IOException {
if (photojs != null) {
html.append("<script type=\"text/javascript\" src=\"");
html.append(photojs);
html.append("\"></script>");
}
}
@Override
public abstract void handleTemplateEvent(TemplateEvent e)
throws IOException;
void processTemplate(String resource, OutputStream out) throws IOException {
getTemplateProcessor().readWriteTemplate(resource, out, this);
}
static class MapCommandLine<T extends AbstractMapMaker> extends
AbstractCommandLine<T> {
public final static String OPT_HTML = "xhtml";
public final static String OPT_JS = "javascript";
public final static String OPT_JS_URL = "Javascript-URL";
public final static String OPT_PHOTO_JS = "photojs";
public final static String OPT_PHOTO_URL = "Photo-URL";
public final static String OPT_TITLE = "title";
public final static String OPT_API_KEY = "api-key";
public final static String OPT_NO_TRK = "no-trk";
public final static String OPT_NO_WPT = "no-wpt";
public MapCommandLine(T mmaker, String[] argv) {
super(mmaker, argv);
}
private void processInput(String[] inp) {
try {
if (inp == null || inp.length == 0 || inp.length == 1
&& "-".equals(inp[0])) {
app.addGpx(System.in);
} else {
for (String fgpx : inp) {
InputStream in = new FileInputStream(fgpx);
try {
app.addGpx(in);
} finally {
in.close();
}
}
}
app.print();
} catch (Exception ex) {
app.handleException(null, ex);
System.exit(1);
}
}
@Override
public void execute() {
processArguments();
processInput(getInput());
}
@Override
protected Options createOptions() {
Options options = super.createOptions();
options.addOption(makeOption(OPT_JS, File.class, 1, "file"));
options.addOption(makeOption(OPT_HTML, File.class, 1, "file"));
options.addOption(makeOption(OPT_JS_URL, URL.class, 1, "URL"));
options.addOption(makeOption(OPT_TITLE, String.class, 1, "title"));
options.addOption(makeOption(OPT_PHOTO_JS, URL.class, 1, "URL"));
options.addOption(makeOption(OPT_PHOTO_URL, URL.class, 1, "URL"));
options.addOption(makeOption(OPT_API_KEY, String.class, 1,
"GoogleMapsApiKey"));
options.addOption(makeOption(OPT_NO_TRK, Boolean.class, 0, null));
options.addOption(makeOption('W', OPT_NO_WPT, Boolean.class, 0,
null));
return options;
}
@Override
protected CommandLine processOptions(Options options)
throws org.apache.commons.cli.ParseException, IOException,
java.text.ParseException {
CommandLine cl = super.processOptions(options);
char c;
opt = OPT_API_KEY;
c = opt.charAt(0);
if (cl.hasOption(c)) {
app.setGMapApiKey(cl.getOptionValue(c));
}
/* output options */
opt = OPT_HTML;
c = opt.charAt(0);
if (cl.hasOption(c)) {
app.enableOutput(Output.HTML, cl.getOptionValue(c));
}
opt = OPT_JS;
c = opt.charAt(0);
if (cl.hasOption(c)) {
app.enableOutput(Output.MapJS, cl.getOptionValue(c));
if (!cl.hasOption(OPT_JS_URL.charAt(0))) {
app.setURL(Output.MapJS, cl.getOptionValue(c));
}
}
opt = OPT_JS_URL;
c = opt.charAt(0);
if (cl.hasOption(c)) {
app.setURL(Output.MapJS, cl.getOptionValue(c));
}
/* title option */
opt = OPT_TITLE;
c = opt.charAt(0);
if (cl.hasOption(c)) {
app.setHTMLTitle(cl.getOptionValue(c));
}
/* photo option */
opt = OPT_PHOTO_JS;
c = opt.charAt(0);
if (cl.hasOption(c)) {
app.setPhotoJavascript(cl.getOptionValue(c));
}
opt = OPT_PHOTO_URL;
c = opt.charAt(0);
if (cl.hasOption(c)) {
app.setPhotoURLPrefix(cl.getOptionValue(c));
}
/* no-XXX options */
opt = OPT_NO_TRK;
c = opt.charAt(0);
if (cl.hasOption(c)) {
app.setIncludeTrks(false);
}
c = 'W';
if (cl.hasOption(c)) {
app.setIncludeWpts(false);
}
return cl;
}
}
}