Copy link to clipboard
Copied
Hi community,
this one is a really hard one!
Going back and forth for a couple of days now my brain got completely stucked... so please give me some input or maybe someone can work out a functional script in this case:
A bunch of coordinates (longitude & latitude values) must be transfered onto an Illustrator map to set a dot for each location... I'm stuggeling with creating the right formular to set them right... I'm pretty sure it's just a mathematical logical issue thing...
// the locations in latitude & longitude as vars:
var lat1 = 48.7491239657; //city_1
var lon1 = 9.17012929916; //city_1
var lat2 = 47.6264900005; //city_2
var lon2 = 7.65646999127; //city_2
var lat3 = 49.4682342; //city_3
var lon3 = 8.5611973; //city_3
// the locations manually set on the Illustrator-Map as vars:
var my_x1 = mm(116.60); //city_1
var my_y1 = mm(82.97); //city_1
var my_x2 = mm(39.83); //city_2
var my_y2 = mm(168.54); //city_2
var my_x3 = mm(83.67); //city_3
var my_y3 = mm(29.33); //city_3
//mm to point converter
function mm(n) {
return n * 2.83464567;
}
PS. and as always – only scripting solutions appreceated! 😉
Hallo Nils,
sofern du die entsprechenden Start und Endkoordinaten passend zu deiner Dokumentengröße verfügbar hast, ist es "simpler Dreisatz".
gegeben ist:
ein Dokument in der Größe 200 mm × 297 mm
ein Kartenausschnitt von 9,0° Ost bis 10,0° Ost und 49,0° Nord bis 48,0° Nord
die X-Koordinate von Stuttgart Bad Cannstatt ist fast exakt 9,2° Ost
Berechnung:
also pos X = (X-Koordinate Stuttgart Bad Cannstatt - KarteGeoCoorLinks) * Dokument Rechts / (KarteGeoCoorRechts - KarteGeoCoorLinks)
in mm
pos
@m1b Hi Mark,
thank you so much for this brillant script!
It worked out quite well in the smaller map but not for a larger section as the geometrical distortion will lead to inaccurate results in the outer areas...
never the less this answer is correct as well 🙂
It took a while to figure it out but this is the best aswer as it leaded to a solution for my issue!
Best tip ever was that something might be wrong with my Illustrator file as the coords were almost 30mm off – so I created a new file and there we go!
Next issue were multiple distortions within the scan of the original map so I had to fix it in two ways:
1.) I started by using the solution pixxxelschubser pointed out
2.) I added a factor to multiply the distortions for left to right and top to b
...Copy link to clipboard
Copied
Hi @Nils M. Barner, Edit: I have updated my code below (and thanks to the solveMatrix function from ChatGPT!) and I hope it will work this time! 🙂
The idea is you call the "getTransform" function, giving it your example transformation pairs [ [lat,long], [x, y] ] and it returns a function that you can use thereafter for transforming your coordinates. Let me know if it works in practice!
- Mark
/**
* @file Transform Latitude Longitude.js
* Demo of transforming lat,long coordinates
* into Illustrator coordinates.
*
* Notes:
* - This is a quick, simple method and hardly tested,
* so be careful to check the results.
* - It knows nothing about actual cartography or
* map projections!
* - It works using the supplied example transformations.
*
* @author m1b, chatGPT
* @discussion https://community.adobe.com/t5/illustrator-discussions/illustrator-scripting-transfer-real-life-longitude-amp-latitude-on-illustrator-map-help-needed/m-p/14716499
*/
(function () {
const mm = 2.834645;
// these examples are necessary to calibrate the transformer
var city1 = [48.7491239657, 9.17012929916],
city2 = [47.6264900005, 7.65646999127],
city3 = [49.4682342, 8.5611973];
var city1_pts = [116.60 * mm, 82.97 * mm],
city2_pts = [39.83 * mm, 168.54 * mm],
city3_pts = [83.67 * mm, 29.33 * mm];
// generate a transform function using some examples
var transform = getTransform([
[city1, city1_pts],
[city2, city2_pts],
[city3, city3_pts],
]);
// calculate position of a new city
var city4 = [48.123456, 8.654321],
city4_pts = transform(city4);
$.writeln('city4_pts = ' + city4_pts);
})();
/**
* Returns a transform function that transforms
* [latitude, longitude] into Illustrator [x, y],
* given an array of example `pairs`.
* @author m1b and chatGPT
* @version 2024-07-03
* @param {Array<Array>} pairs - array of [lat_lon, pts] pairs.
* @returns {Function}
*/
function getTransform(pairs) {
var A = [],
B = [];
for (var i = 0; i < pairs.length; i++) {
A.push([pairs[i][0][0], pairs[i][0][1], 1, 0, 0, 0]);
A.push([0, 0, 0, pairs[i][0][0], pairs[i][0][1], 1]);
B.push(pairs[i][1][0], pairs[i][1][1]);
}
// Solve for the affine transformation matrix using matrix algebra (A * X = B)
var X = solveMatrix(A, B);
var m = [
[X[0], X[1], X[2]],
[X[3], X[4], X[5]],
];
// Transform function to apply the affine transformation
return (
function transform(latLon) {
return [
m[0][0] * latLon[0] + m[0][1] * latLon[1] + m[0][2],
m[1][0] * latLon[0] + m[1][1] * latLon[1] + m[1][2],
];
}
);
};
/**
* Solves a system of linear equations using Gaussian elimination.
*
* This function solves the system of equations represented by the augmented matrix [A|B],
* where A is an n x n matrix and B is an n x 1 vector.
*
* @author chatGPT
* @version 2024-07-03
* @param {Array<Array<Number>>} A - The coefficient an n x n matrix of the system of equations.
* @param {Array<Number>} B - The constant terms vector of the system of equations.
* @returns {Array<Number>} - The solution vector X such that A * X = B.
*/
function solveMatrix(A, B) {
var n = A.length;
for (var i = 0; i < n; i++)
A[i].push(B[i]);
for (var i = 0; i < n; i++) {
var maxRow = i;
for (var k = i + 1; k < n; k++)
if (Math.abs(A[k][i]) > Math.abs(A[maxRow][i]))
maxRow = k;
var tmp = A[maxRow];
A[maxRow] = A[i];
A[i] = tmp;
for (var k = i + 1, c; k < n; k++) {
c = -A[k][i] / A[i][i];
for (var j = i; j < n + 1; j++) {
if (i === j)
A[k][j] = 0;
else
A[k][j] += c * A[i][j];
}
}
}
var X = new Array(n);
for (var i = n - 1; i >= 0; i--) {
X[i] = A[i][n] / A[i][i];
for (var k = i - 1; k >= 0; k--)
A[k][n] -= A[k][i] * X[i];
}
return X;
};
Copy link to clipboard
Copied
@mark: is this the fixed version now? or should I still ignore?
Copy link to clipboard
Copied
Please take it for a test drive!
Copy link to clipboard
Copied
Sorry about posting my faulty code before. I've updated it now, thanks to some help from ChatGPT!, and tested a bit more and it gives the expected results now. - Mark
Copy link to clipboard
Copied
@m1b: Hi Mark,
I tried your Script but coords are still far off...
Just tried to figure out why but I got even more confused now...
Please find a simplified demo .ai file attached (please rename the .pdf > .ai) so you can try on your own...
My thoughts on the case:
Something might be twisted (lang/long, x/y, ...) so values get mixed up...
Here's my script so far (just added some more functions and vars to it):
/**
* @File Transform Latitude Longitude.js
* Demo of transforming lat,long coordinates
* into Illustrator coordinates.
*
* Notes:
* - This is a quick, simple method and hardly tested,
* so be careful to check the results.
* - It knows nothing about actual cartography or
* map projections!
* - It works using the supplied example transformations.
*
* @author m1b, chatGPT
* @discussion https://community.adobe.com/t5/illustrator-discussions/illustrator-scripting-transfer-real-life-longitude-amp-latitude-on-illustrator-map-help-needed/m-p/14716499
*/
#target illustrator
////// first things first and basic checks
var docRef = app.activeDocument;
var artboardRef = docRef.artboards;
var scriptRef = new File($.fileName).path;
var separator = '\\';
var lineBreak = '\r';
var CSVref = '';
var GoodToGo = false;
var thisX;
var thisY;
var xStartPos = 0; //// just in case to move
var yStartPos = 0; //// just in case to move
var hsdDiameter = mm(1.65); //// size of hotspot dot
var hsdStrokeWidth = 0.5; //// stroke weight of hotspot dot
var hsdColor = makeCMYKColor(10,100,90,0);
var hsdStrokeColor = makeCMYKColor(100,0,0,0);
(function () {
const mm = 2.834645;
// these examples are necessary to calibrate the transformer
var city1 = [48.7491239657, 9.17012929916], //// this ist Stuttgart //_latitude (north/south > Y) then _longitude (west/east > X)
city2 = [47.6264900005, 7.65646999127], //// this is Lörrach
city3 = [49.4682342, 8.5611973]; //// this is Mannheim
// var city4 = [48.7493055, 8.2197765], //// this is Baden-Baden
// var city5 = [48.984742, 8.403163], //// this is Karlsruhe
// var city6 = [48.2315269, 9.8833368], //// this is Ulm (might be a bit off)
// var city7 = [49.5985400069, 9.69389004153], //// this is Tauberbischofsheim
var city1_pts = [152,887 * mm, 99,701 * mm], //// this is Stuttgart //_x then _y
city2_pts = [76,123 * mm, 185,265 * mm], //// this is Lörrach
city3_pts = [119,954 * mm, 46,053 * mm]; //// this is Mannheim
// generate a transform function using some examples
var transform = getTransform([
[city1, city1_pts],
[city2, city2_pts],
[city3, city3_pts],
]);
// calculate position of a new city
var city4 = [48.7491239657, 9.17012929916], // this is Baden-Baden
city4_pts = transform(city4);
var city5 = [48.984742, 8.403163], //// this is Karlsruhe
city5_pts = transform(city5);
var city7 = [49.5985400069, 9.69389004153], //// this is Tauberbischofsheim
city7_pts = transform(city7);
$.writeln('city4_pts = ' + city4_pts);
alert(city4_pts);
var thisX = city4_pts[0] + xStartPos;
var thisY = city4_pts[1] + yStartPos;
createHotSpot(thisX,thisY,hsdDiameter);
alert(city5_pts);
var thisX = city5_pts[0] + xStartPos;
var thisY = city5_pts[1] + yStartPos;
createHotSpot(thisX,thisY,hsdDiameter);
alert(city7_pts);
var thisX = city7_pts[0] + xStartPos;
var thisY = city7_pts[1] + yStartPos;
createHotSpot(thisX,thisY,hsdDiameter);
})();
// create hotspot
function createHotSpot(xHS, yHS, diaHS) {
var myX = xHS + diaHS/2; //for center point
var myY = yHS - diaHS/2; //for center point
var thisHotSpot = docRef.activeLayer.pathItems.ellipse(myX, myY, diaHS, diaHS);
thisHotSpot.fillColor = hsdColor;
thisHotSpot.stroked = true;
thisHotSpot.strokeWidth = hsdStrokeWidth;
thisHotSpot.strokeColor = hsdStrokeColor;
return thisHotSpot; // Return the created thisHotSpot
}
/**
* Returns a transform function that transforms
* [latitude, longitude] into Illustrator [x, y],
* given an array of example `pairs`.
* @author m1b and chatGPT
* @version 2024-07-03
* @Param {Array<Array>} pairs - array of [lat_lon, pts] pairs.
* @Returns {Function}
*/
function getTransform(pairs) {
var A = [],
B = [];
for (var i = 0; i < pairs.length; i++) {
A.push([pairs[i][0][0], pairs[i][0][1], 1, 0, 0, 0]);
A.push([0, 0, 0, pairs[i][0][0], pairs[i][0][1], 1]);
B.push(pairs[i][1][0], pairs[i][1][1]);
}
// Solve for the affine transformation matrix using matrix algebra (A * X = B)
var X = solveMatrix(A, B);
var m = [
[X[0], X[1], X[2]],
[X[3], X[4], X[5]],
];
// Transform function to apply the affine transformation
return (
function transform(latLon) {
return [
m[0][0] * latLon[0] + m[0][1] * latLon[1] + m[0][2],
m[1][0] * latLon[0] + m[1][1] * latLon[1] + m[1][2],
];
}
);
};
/**
* Solves a system of linear equations using Gaussian elimination.
*
* This function solves the system of equations represented by the augmented matrix [A|B],
* where A is an n x n matrix and B is an n x 1 vector.
*
* @author chatGPT
* @version 2024-07-03
* @Param {Array<Array<Number>>} A - The coefficient an n x n matrix of the system of equations.
* @Param {Array<Number>} B - The constant terms vector of the system of equations.
* @Returns {Array<Number>} - The solution vector X such that A * X = B.
*/
function solveMatrix(A, B) {
var n = A.length;
for (var i = 0; i < n; i++)
A[i].push(B[i]);
for (var i = 0; i < n; i++) {
var maxRow = i;
for (var k = i + 1; k < n; k++)
if (Math.abs(A[k][i]) > Math.abs(A[maxRow][i]))
maxRow = k;
var tmp = A[maxRow];
A[maxRow] = A[i];
A[i] = tmp;
for (var k = i + 1, c; k < n; k++) {
c = -A[k][i] / A[i][i];
for (var j = i; j < n + 1; j++) {
if (i === j)
A[k][j] = 0;
else
A[k][j] += c * A[i][j];
}
}
}
var X = new Array(n);
for (var i = n - 1; i >= 0; i--) {
X[i] = A[i][n] / A[i][i];
for (var k = i - 1; k >= 0; k--)
A[k][n] -= A[k][i] * X[i];
}
return X;
};
function mm(n) {
return n * 2.83464567;
}
function makeCMYKColor(c,m,y,k){
var ink = new CMYKColor();
ink.cyan = c;
ink.magenta = m;
ink.yellow = y;
ink.black = k;
return ink;
}
Thanks
Nils
Copy link to clipboard
Copied
Hi @Nils M. Barner, thanks for the map—that helped. Actually, my script seemed to work fine. This is what I did...
First, I entered three cities from your map: Lörrach, Mannheim and Ulm, both lat/long and map coordinates (taken as the centre of the circle on your map. These are to calibrate the transform function. Here is the data I added to the script (replacing all the city1, city2, ... stuff):
// these examples are necessary to calibrate the transformer
const lörrach = [47.6168, 7.6691],
mannheim = [49.4875, 8.4660],
ulm = [48.4011, 9.9876];
const lörrach_pts = [76.123 * mm, 185.265 * mm],
mannheim_pts = [119.954 * mm, 46.053 * mm],
ulm_pts = [191.809 * mm, 127.151 * mm];
// generate a transform function using some examples
var transform = getTransform([
[lörrach, lörrach_pts],
[mannheim, mannheim_pts],
[ulm, ulm_pts],
]);
// calculate position of a new city
var stuttgart = [48.7758, 9.1829],
heilbronn = [49.1427, 9.2109];
// now transform into map coordinates
var stuttgart_pts = transform(stuttgart),
heilbronn_pts = transform(heilbronn);
$.writeln('stuttgart_pts = ' + stuttgart_pts);
$.writeln('heilbronn_pts = ' + heilbronn_pts);
Then I entered two new cities—Stuttgart and Heilbronn—lat/long and used the transform function to give me the coordinates in pts. Console output:
stuttgart_pts = 434.552869517851,281.040327929184
heilbronn_pts = 441.087488743414,203.598201369798
Then I just positioned a green circle at those two points on your map. This is what I got:
So not too bad at all. Does it work for you?
- Mark
Copy link to clipboard
Copied
@m1b Hi Mark,
thank you so much for this brillant script!
It worked out quite well in the smaller map but not for a larger section as the geometrical distortion will lead to inaccurate results in the outer areas...
never the less this answer is correct as well 🙂
Copy link to clipboard
Copied
Hi @Nils M. Barner, did you try just putting in 4 points at extreme points? Eg the corners only? Similar to @pixxxelschubser's idea? I wonder if that would be more accurate?
Or maybe we could calibrate the transform each time, using only the three "mapped" cities closest to the unknown city? Perhaps this would adequately minimize the inaccuracies?
You can tell I am only guessing here because I am very ignorant about this domain.
- Mark
Copy link to clipboard
Copied
Hallo Nils,
hast du eventuell auch die deinen Dokumentengrenzen entsprechenden Geo-Koordinaten? Denn dann könnte man doch normalerweise die City-Koordinaten durch Dreisatz-Berechnung ermitteln.
Oder habe ich die Aufgabenstellung nicht richtig verstanden?
Copy link to clipboard
Copied
Hi pixxxelschubser 😉
I was thinking of setting up a x/y start coordinate to give all points a fixed reference...
Wie meinst du genau mit der Dreisatzberechnung?
Copy link to clipboard
Copied
Hallo Nils,
sofern du die entsprechenden Start und Endkoordinaten passend zu deiner Dokumentengröße verfügbar hast, ist es "simpler Dreisatz".
gegeben ist:
ein Dokument in der Größe 200 mm × 297 mm
ein Kartenausschnitt von 9,0° Ost bis 10,0° Ost und 49,0° Nord bis 48,0° Nord
die X-Koordinate von Stuttgart Bad Cannstatt ist fast exakt 9,2° Ost
Berechnung:
also pos X = (X-Koordinate Stuttgart Bad Cannstatt - KarteGeoCoorLinks) * Dokument Rechts / (KarteGeoCoorRechts - KarteGeoCoorLinks)
in mm
pos X = (9,2 - 9) * 200 / (10 - 9)
pos X = (0,2) * 200 / (1)
pos X = 40
bzw in pt
pos X = (9,2 - 9) * 566.929 / (10 - 9)
pos X = (0,2) * 566.929 / (1)
pos X = 113.385826771653
Copy link to clipboard
Copied
Hallo Nils,
die Variante mit den Start- und Endkoordinaten kommt für dich nicht in Frage?
Copy link to clipboard
Copied
It took a while to figure it out but this is the best aswer as it leaded to a solution for my issue!
Best tip ever was that something might be wrong with my Illustrator file as the coords were almost 30mm off – so I created a new file and there we go!
Next issue were multiple distortions within the scan of the original map so I had to fix it in two ways:
1.) I started by using the solution pixxxelschubser pointed out
2.) I added a factor to multiply the distortions for left to right and top to button
3.) after fixing this there was still a lot of distortion going on in the corners so additionally I devided the whole map into 4 areas and scaled each of these sections individually based on the distance from the center of the artboard + some additional factors for each direction...
This finally produced the desired, precise result!
I will not post a finished script here, as the result is too special and only works smoothly for this one map. Hope the way to get there will help anyone seeking for a similar solution.
Nils