mirror of
https://github.com/csharpee/Map-Projections.git
synced 2025-12-12 00:00:17 -05:00
Finally! Edcent-looking maps!
I got the coastlines to correctly rebind to themselves when crossing a neatline. My projection code finally produces decent maps without post-processing! Now to fulfill my dream of replacing all the Tissot's Indicatrices images on Wikipedia!
This commit is contained in:
parent
075ef87e99
commit
7bcc659750
@ -183,16 +183,13 @@ public class MapDesignerVector extends MapApplication {
|
|||||||
|
|
||||||
List<Path> theMap = new LinkedList<Path>();
|
List<Path> theMap = new LinkedList<Path>();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Path path0: input) {
|
for (Path pathS: input) {
|
||||||
updateProgress(i, input.numCurves());
|
updateProgress(i, input.numCurves());
|
||||||
if (path0.size() <= step) continue; //don't bother drawing singular points
|
if (pathS.size() <= step) continue; //don't bother drawing singular points
|
||||||
Path path1 = new Path();
|
Path pathP = new Path();
|
||||||
int counter = 0;
|
int j = 0;
|
||||||
for (Command cmdS: path0) {
|
while (j < pathS.size()) {
|
||||||
counter --; //skip the requisite number of 'L's
|
Command cmdS = pathS.get(j);
|
||||||
if (counter > 0 && cmdS.type != 'M' && cmdS.type != 'Z') continue;
|
|
||||||
counter = step;
|
|
||||||
|
|
||||||
Command cmdP = new Command(cmdS.type, new double[cmdS.args.length]);
|
Command cmdP = new Command(cmdS.type, new double[cmdS.args.length]);
|
||||||
for (int k = 0; k < cmdS.args.length; k += 2) {
|
for (int k = 0; k < cmdS.args.length; k += 2) {
|
||||||
double[] coords = proj.project(cmdS.args[k+1], cmdS.args[k], aspect);
|
double[] coords = proj.project(cmdS.args[k+1], cmdS.args[k], aspect);
|
||||||
@ -203,9 +200,17 @@ public class MapDesignerVector extends MapApplication {
|
|||||||
if (Double.isNaN(cmdP.args[k]) || Double.isNaN(cmdP.args[k+1]))
|
if (Double.isNaN(cmdP.args[k]) || Double.isNaN(cmdP.args[k+1]))
|
||||||
System.err.println(getProjection()+" returns "+cmdP.args[k]+","+cmdP.args[k+1]+" at "+cmdS.args[k+1]+","+cmdS.args[k]+"!");
|
System.err.println(getProjection()+" returns "+cmdP.args[k]+","+cmdP.args[k+1]+" at "+cmdS.args[k+1]+","+cmdS.args[k]+"!");
|
||||||
}
|
}
|
||||||
path1.add(cmdP); //TODO: if I was smart, I would divide landmasses that hit an interruption so that I didn't get those annoying lines that cross the map, and then run adaptive resampling to make sure the cuts look clean and not polygonal (e.g. so Antarctica extends all the way to the bottom), but that sounds really hard.
|
pathP.add(cmdP); //TODO: if I was smart, I would divide landmasses that hit an interruption so that I didn't get those annoying lines that cross the map, and then run adaptive resampling to make sure the cuts look clean and not polygonal (e.g. so Antarctica extends all the way to the bottom), but that sounds really hard.
|
||||||
|
|
||||||
|
for (int k = 0; k < step; k ++) { //increment j by at least 1 and at most step
|
||||||
|
if (k != 0 && (j >= pathS.size() - 1 || pathS.get(j).type == 'M'
|
||||||
|
|| pathS.get(j).type == 'Z'))
|
||||||
|
break; //but pause for every moveto and closepath, and for the last command in the path
|
||||||
|
else
|
||||||
|
j ++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
theMap.add(path1);
|
theMap.add(pathP);
|
||||||
|
|
||||||
i ++;
|
i ++;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -280,7 +280,8 @@ public class SVGMap implements Iterable<SVGMap.Path> {
|
|||||||
|
|
||||||
out.write(replacePlaceholders(formatIterator.next(), inWidth/inHeight));
|
out.write(replacePlaceholders(formatIterator.next(), inWidth/inHeight));
|
||||||
while (curveIterator.hasNext()) {
|
while (curveIterator.hasNext()) {
|
||||||
out.write(breakWraps(curveIterator.next()).toString(inMinX, inMaxY, vbMinX, vbMinY,
|
out.write(closePaths(breakWraps(curveIterator.next())).toString(
|
||||||
|
inMinX, inMaxY, vbMinX, vbMinY,
|
||||||
Math.max(vbWidth, vbHeight)/Math.max(inWidth, inHeight)));
|
Math.max(vbWidth, vbHeight)/Math.max(inWidth, inHeight)));
|
||||||
out.write(formatIterator.next());
|
out.write(formatIterator.next());
|
||||||
}
|
}
|
||||||
@ -325,8 +326,8 @@ public class SVGMap implements Iterable<SVGMap.Path> {
|
|||||||
|
|
||||||
char type = continuous.get(i).type;
|
char type = continuous.get(i).type;
|
||||||
if ((Double.isNaN(lens[0]) || lens[1] > 20*lens[0]) //and compare it to the last two lengths
|
if ((Double.isNaN(lens[0]) || lens[1] > 20*lens[0]) //and compare it to the last two lengths
|
||||||
&& (Double.isNaN(lens[2]) || lens[1] > 20*lens[2]))
|
&& (Double.isNaN(lens[2]) || lens[1] > 20*lens[2])) //if both sides are far longer or nonexistent
|
||||||
type = 'M';
|
type = 'M'; //break this line
|
||||||
|
|
||||||
broken.add(new Command(type, continuous.get(i).args.clone()));
|
broken.add(new Command(type, continuous.get(i).args.clone()));
|
||||||
lens[0] = lens[1];
|
lens[0] = lens[1];
|
||||||
@ -337,6 +338,44 @@ public class SVGMap implements Iterable<SVGMap.Path> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Path closePaths(Path open) { //replace plain loops with 'Z's and combine connected parts
|
||||||
|
List<Path> parts = new ArrayList<Path>();
|
||||||
|
Path currentPart = null;
|
||||||
|
for (Command cmd: open) { //start by breaking the Path into parts,
|
||||||
|
if (cmd.type == 'M') { //separated by movetos
|
||||||
|
if (currentPart != null)
|
||||||
|
parts.add(currentPart);
|
||||||
|
currentPart = new Path();
|
||||||
|
}
|
||||||
|
currentPart.add(cmd);
|
||||||
|
}
|
||||||
|
parts.add(currentPart);
|
||||||
|
|
||||||
|
Path closed = new Path();
|
||||||
|
for (int i = 0; i < parts.size(); i ++) { //now look through those parts
|
||||||
|
Path partI = parts.get(i);
|
||||||
|
if (partI.size() > 1
|
||||||
|
&& Arrays.equals(partI.get(0).args, partI.get(partI.size()-1).args)) { //if it is self-enclosing
|
||||||
|
partI.set(partI.size()-1, new Command('Z', new double[0])); //give it a closepath and send it on its way
|
||||||
|
}
|
||||||
|
else { //if it is open
|
||||||
|
for (int j = i+1; j < parts.size(); j ++) { //look to see if there is anything that completes it
|
||||||
|
Path partJ = parts.get(j);
|
||||||
|
if (Arrays.equals(partI.get(0).args, partJ.get(partJ.size()-1).args)) { //if so,
|
||||||
|
partI.remove(0); //remove the useless moveto
|
||||||
|
partJ.addAll(partI); //combine them
|
||||||
|
partI = partJ;
|
||||||
|
parts.remove(j); //don't look at J anymone; it has been absorbed.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closed.addAll(partI); //now turn in whatever you've got
|
||||||
|
}
|
||||||
|
return closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static boolean isNonELetter(char c) {
|
private static boolean isNonELetter(char c) {
|
||||||
return (c >= 'A' && c <= 'Z' && c != 'E') || (c >= 'a' && c <= 'z' && c != 'e');
|
return (c >= 'A' && c <= 'Z' && c != 'E') || (c >= 'a' && c <= 'z' && c != 'e');
|
||||||
}
|
}
|
||||||
@ -445,10 +484,6 @@ public class SVGMap implements Iterable<SVGMap.Path> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path(Command command) {
|
|
||||||
// TODO: Implement this
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString(
|
public String toString(
|
||||||
double inMinX, double inMaxY, double outMinX, double outMinY, double outScale) {
|
double inMinX, double inMaxY, double outMinX, double outMinY, double outScale) {
|
||||||
String s = "";
|
String s = "";
|
||||||
@ -489,7 +524,7 @@ public class SVGMap implements Iterable<SVGMap.Path> {
|
|||||||
else
|
else
|
||||||
s += formatDouble(outMinY + (inMaxY-args[i])*outScale)+",";
|
s += formatDouble(outMinY + (inMaxY-args[i])*outScale)+",";
|
||||||
}
|
}
|
||||||
return s.substring(0, s.length()-1);
|
return s.substring(0, Math.max(1, s.length()-1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user