package geo;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.imageio.ImageIO;
public class CreateBGImage {
int width = 1024;
int height = 768;
double viewPortOffsetX = 0;
double viewPortOffsetY = 0;
double[] m_fBGImageBox = { -180, -90, 180, 90 };
String svgFile = "/home/remco/data/map/TM_WORLD_BORDERS-0.3.svg";
List<String> m_sLabels;
List<Integer> m_nLabels;
List<List<Double>> m_contours;
int svgHeight, svgWidth;
float linewidth = 1f;
public BufferedImage bgImage;
String bgFile = null;
private void parseArgs(String[] args) {
int i = 0;
try {
while (i < args.length) {
int iOld = i;
if (i < args.length - 1) {
if (args[i].equals("")) {
i += 1;
} else if (args[i].equals("-geo")) {
try {
Pattern pattern = Pattern
.compile(".*\\(([0-9\\.Ee-]+),([0-9\\.Ee-]+)\\)x\\(([0-9\\.Ee-]+),([0-9\\.Ee-]+)\\).*");
Matcher matcher = pattern.matcher(args[i+1]);
matcher.find();
double x1 = Float.parseFloat(matcher.group(1));
double x2 = Float.parseFloat(matcher.group(2));
double x3 = Float.parseFloat(matcher.group(3));
double x4 = Float.parseFloat(matcher.group(4));
m_fBGImageBox[1] = Math.min(x1,x3);
m_fBGImageBox[0] = Math.min(x2,x4);
m_fBGImageBox[3] = Math.max(x1,x3);
m_fBGImageBox[2] = Math.max(x2,x4);
} catch (Exception e) {
System.err.println("Could not parse geo argument, expected (lat1,long1)x(lat2,long2), using default (-90,-180)x(90,180)");
final double[] fBGImageBox = { -180, -90, 180, 90 };
m_fBGImageBox = fBGImageBox;
}
i += 2;
} else if (args[i].equals("-svg")) {
svgFile = args[i+1];
i += 2;
} else if (args[i].equals("-bgimage")) {
bgFile = args[i+1];
i += 2;
} else if (args[i].equals("-w")) {
width = Integer.parseInt(args[i+1]);
i += 2;
} else if (args[i].equals("-h")) {
height = Integer.parseInt(args[i+1]);
i += 2;
} else if (args[i].equals("-lw")) {
linewidth = Float.parseFloat(args[i+1]);
i += 2;
} else if (args[i].equals("-help")) {
printUsageAndExit();
}
if (i == iOld) {
throw new Exception("Wrong argument");
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("Error parsing command line arguments: " + Arrays.toString(args)
+ "\nArguments ignored\n\n");
}
}
private void createImage() throws IOException {
parseSVGFile();
drawMap();
saveFile();
}
private void parseSVGFile() {
try {
System.out.println("Reading " + svgFile);
m_sLabels = new ArrayList<String>();
m_nLabels = new ArrayList<Integer>();
m_contours = new ArrayList<List<Double>>();
long nLength = new File(svgFile).length();
MappedByteBuffer in = new FileInputStream(svgFile).getChannel().map(FileChannel.MapMode.READ_ONLY, 0, nLength);
int i = 0;
while (i < nLength) {
char c = (char) in.get(i++);
if (c == '<') {
c = (char) in.get(i++);
if (c == 'g') {
// we are in a <g> element
do {
c = (char) in.get(i++);
} while (c != 'i' && c !='>');
if (c == 'i' && in.get(i++) == 'd' && in.get(i++) == '=') {
char delim = (char) in.get(i++);
String sID = "";
c = (char) in.get(i++);
while (c != delim) {
sID += c;
c = (char) in.get(i++);
}
m_sLabels.add(sID);
m_nLabels.add(m_contours.size());
}
} else if (c == 'p' && in.get(i++) == 'a' && in.get(i++) == 't' && in.get(i++) == 'h') {
// we are in a <path> element
char prev = ' ';
do {
prev = c;
c = (char) in.get(i++);
} while ((c != 'd' && c !='>') || (c == 'd' && prev == 'i'));
boolean relativeMoves = false;
if (c == 'd' && in.get(i++) == '=') {
List<Double> contour = new ArrayList<Double>(32);
char delim = (char) in.get(i++);
c = (char) in.get(i++);
StringBuffer buf = new StringBuffer();
while (c != delim) {
if (c == 'm' || c == 'l') relativeMoves = true;
//if (c == 'L') relativeMoves = false;
if (c == ' ' || c == 'L' || c == 'l' || c == 'z' || c == 'M' || c == 'm' || c == ',') {
if (buf.length() > 0) {
double f = Double.parseDouble(buf.toString());
contour.add(f);
if (relativeMoves && contour.size() > 2) {
int k = contour.size() - 1;
contour.set(k, contour.get(k - 2) + f);
}
buf.delete(0, buf.length());
}
} else {
buf.append(c);
}
c = (char) in.get(i++);
}
if (contour.size() > 1) {
m_contours.add(contour);
}
}
} else if (c == 's' && in.get(i++) == 'v' && in.get(i++) == 'g') {
// we are in the svg element
String s = "";
while ((c = (char) in.get(i++)) != '>') {
s += c;
}
int k = s.indexOf("width=");
String s2 = s.substring(k + 7);
k = s2.indexOf("\""); if ( k<0) k = s2.indexOf("'");
s2 = s2.substring(0, k);
svgWidth = Integer.parseInt(s2);
k = s.indexOf("height=");
s2 = s.substring(k + 8);
k = s2.indexOf("\""); if ( k<0) k = s2.indexOf("'");
s2 = s2.substring(0, k);
svgHeight = Integer.parseInt(s2);
k = s.indexOf("viewBox=");
if (k > 0) {
s2 = s.substring(k + 9);
k = s2.indexOf("\""); if ( k<0) k = s2.indexOf("'");
s2 = s2.substring(0, k);
String [] strs = s2.split(" ");
viewPortOffsetX = Double.parseDouble(strs[0]);
viewPortOffsetY = Double.parseDouble(strs[1]);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void drawMap() throws IOException {
bgImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) bgImage.getGraphics();
g.setColor(Color.white);
// mirror over x-axis
g.setTransform(new AffineTransform(1.0, 0.0, 0.0, -1.0, 0.0, height));
g.fillRect(0, 0, width, height);
if (bgFile != null) {
try {
BufferedImage bg = ImageIO.read(new File(bgFile));
int w = bg.getWidth();
int h = bg.getHeight();
int sx1 = (int)(w * (m_fBGImageBox[0] + 180) / 360.0);
int sy1 = h - (int)(h * (m_fBGImageBox[1] + 90) / 180.0);
int sx2 = (int)(w * (m_fBGImageBox[2] + 180) / 360.0);
int sy2 = h - (int)(h * (m_fBGImageBox[3] + 90) / 180.0);
g.drawImage(bg, 0, 0, width, height, sx1, sy1, sx2, sy2, null);
} catch (Exception e) {
e.printStackTrace();
}
}
g.setStroke(new BasicStroke(linewidth));
int [] xPoints;
int [] yPoints;
double fScaleX = width * 360.0/(m_fBGImageBox[2] - m_fBGImageBox[0]) / svgWidth;
double fScaleY = height * 180.0/(m_fBGImageBox[3] - m_fBGImageBox[1]) / svgHeight;
double fOffsetX = svgWidth * (m_fBGImageBox[0] + 180) / 360.0;
double fOffsetY = svgHeight* (m_fBGImageBox[1] + 90) / 180.0;
for (int iContour = 0; iContour < m_contours.size(); iContour++) {
List<Double> contour = m_contours.get(iContour);
int nPoints = contour.size() / 2;
// if (nPoints >= xPoints.length) {
xPoints = new int[nPoints];
yPoints = new int[nPoints];
// }
// double [] fPoint = new double[2];
for (int j = 0; j < contour.size(); j += 2) {
double fLong = contour.get(j);
double fLat = contour.get(j+1);
xPoints[j / 2] = (int) (fScaleX * (fLong - viewPortOffsetX - fOffsetX));
yPoints[j / 2] = (int) (fScaleY * (svgHeight + viewPortOffsetY - fLat - fOffsetY));
}
g.setColor(Color.black);
g.drawPolygon(xPoints, yPoints, nPoints);
}
}
private void saveFile() {
DecimalFormat f = new DecimalFormat("###.##");
String fileName = "bg(" + f.format(m_fBGImageBox[1]) + "," +
f.format(m_fBGImageBox[0]) + ")x(" +
f.format(m_fBGImageBox[3]) + "," +
f.format(m_fBGImageBox[2]) + ").png";
try {
File file = new File(fileName);
System.out.println("Writing file " + file.getPath());
ImageIO.write(bgImage, "png", file);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void printUsageAndExit() {
System.out.println("Create background image for DensiTree from svg map file");
System.out.println("-svg <file>: svg map file");
System.out.println("-geo (lat1,long1)x(lat2,long2): target area of map");
System.out.println("-w <int>: width in number of pixels in image");
System.out.println("-h <int>: height in number of pixels in image");
System.out.println("-lw <float>: line width");
System.out.println("-bgimage <file>: bitmap file used as background");
System.out.println("-help: show this message");
System.exit(0);
}
public static void main(String[] args) throws IOException {
System.out.println("Processing");
CreateBGImage bgImage = new CreateBGImage();
bgImage.parseArgs(args);
bgImage.createImage();
System.out.println("Done!");
}
}