How to color a map based on data?

Community Beginner ,
Jan 29, 2021

Copy link to clipboard

Copied

Hey everyone,

I hope someone can help me with my problem! I'm a journalist and very new to Illustrator and coding.

 

I have a map, that I converted from a geoJSON file made up of 555 Groups, which I all renamed to a certain areacode (f.e. 21100000) that is also the name of the first column of my spreadsheet. In a CSV-file it looks like this 21100000;36,672991;38,52744966;7,525960381;11,42868801;2,084990922;0,124446621;0,36109921;0;1,305669462;0;0,161168574;0,159128466

 

Every column, except the first one, should have a color adjusted to it (second one is red, third one is black)

 

I want my script to select the highest value (in this case 38,52744966) and adjust the Fill Color of the Group (in this case 21100000) with the adjusted color of the column (in this case column 3 = black).

 

In a second step, I want to set the opacity of the Group to a percentage linked to the value of the column, while a value of 70 would be 100% and a value of 25 being 20%

 

Would be a blast if someone has a solution to this, or can tell me if that is even possible, so I can pay someone to write the script for me. I am willing to learn how to write scripts myself though, if someone has a guide or something I could follow, I'd be very greatful aswell.

 

TOPICS
How to, Scripting

Views

175

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more

1 Correct Answer

Adobe Community Professional , Jan 30, 2021
Silly-V Adobe Community Professional , Jan 30, 2021
You can still do both opacity and color in just one sheet and only have one script, but you'd want to ensure that your spreadsheet has distinct columns that contain the data that is going to be used by the script. So in your case as you want to assign a swatch, you can either have your document contain swatches named "1" and "2", and their color would be the color values you consider to be red or black, or you can have an extra column that has a formula that turns one of those numbers into a str...

Likes

Translate

Translate
Jump to answer Jump to answer
Adobe Community Professional ,
Jan 29, 2021

Copy link to clipboard

Copied

Sounds interesting, but it is hard to tell whether you are using a comma-delimited or semicolon-delimited csv. Do you have a screenshot of your data in a spreadsheet editor?

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Community Beginner ,
Jan 30, 2021

Copy link to clipboard

Copied

Hey, it's semicolon-delimited. The values use a decimal comma instead of a decimal point, which I could change, if that's a problem.

screenshotwahl.png

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Adobe Community Professional ,
Jan 30, 2021

Copy link to clipboard

Copied

Ok, so which columns are supposed to be the colors again?

And in those color columns, the colors are going to be rgb or cmyk swatches?

The opacity statement I am not able to comprehend as if 70 were 100% then there's no way 25 would be 20%. It should be more 35%. (right?)

I will recommend that you handle as much data transformation in a spreadsheet program as much as possible and help reduce scripting features needed so there will be more testable accuracy in the whole process. For example, rather than build data-transformation logic into a script that makes sense of your data, create instead a more rudimentary script that takes in data as is and performs operations on artwork using the data raw or with minor processing such as turning a text-string into a number.

So, if you are able to use formulas to add some columns which calculate your preferred opacity values even if they are arbitrary or result of some unknown calculations, you will end up with a csv that you can just use numbers 0-100 from for your opacity. And then it's easier to write a script to just find items and set their opacity to just the incoming number. Then when the script isn't working right and there's bugs in it, there will be less question about the source of the bug because data transformation has been separated from art processing and you can verify data or the script code as a responsible source for errors much more quickly.

 

Otherwise, there is more explanation needed regarding the columns and colors, it's hard to understand what colors you want to apply to what objects, if they are color values or color swatch names which exist in your document.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Community Beginner ,
Jan 30, 2021

Copy link to clipboard

Copied

Hey, you are right, it's easier to manage the data in excel instead of a script, I haven't thought about this.

 

So I could basically make two spreadsheets and scripts. One for the fill, one for the opacity.

 

I would wrangle my data in excel so I only have the area code and an RBG color associated with it. That would mostly be black or red, 1 or 2 in my spreadsheet. So I'd have something like this

 

211000001
231001342

 

How would a script look like that just associates a RBG color with a different value and colorizes my groups in AI accordingly?

 

For the second spreadsheet I will translate the value in my spreadsheet to the opacity I want for my groups.

 

So that would be two quite rudimentary scripts. But I still don't know how to even go on about using a script in AI and how to translate my thoughts into code.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Adobe Community Professional ,
Jan 30, 2021

Copy link to clipboard

Copied

You can still do both opacity and color in just one sheet and only have one script, but you'd want to ensure that your spreadsheet has distinct columns that contain the data that is going to be used by the script. So in your case as you want to assign a swatch, you can either have your document contain swatches named "1" and "2", and their color would be the color values you consider to be red or black, or you can have an extra column that has a formula that turns one of those numbers into a string because it would look up the text "RGB Red" (or rgb, whatever) in some other sheet or table that has a "1" matching with the string "RGB Red". A brute way would be to select the column and do a find-and-replace to change "1" to "RGB Red" so that you don't have to rename your document swatches to "1". This is useful in cases where you get a lot of data imported from somewhere and you don't want to change your document swatch names to "1" and "2" necessarily but also can handle cases where another type of recurring import has "A" instead of "1" where it means to be red. There you can edit your formula or its data source to contain multiple keys to match to certain strings, so you can have entries like

1

RGB Red

A

RGB Red

2

RGB Black

BLK

RGB Black

 

The new formula-powered column would be sort of like this:

Item Name

Color Key

Color

45678900

1

RGB Red

32165465

2

RGB Black

32134650

2

RGB Black

The opacity could be handled in a similar way, and you can have your Illustrator-specific columns in some spot where you can quickly look and see. Or you are free to use any columns in any order, because in the script you can find your desired column by the name of its header. (Otherwise you'll have to hard-code an index-based lookup which makes it more tedious if the columns ever need to change order: you'll have to always go back to your script and fix it even when the change is only data.) I would recommend using a single csv instead of multiple ones because it's better to see the name of the shape, the color and opacity all in one place so when you highlight a row you can see exactly what appears rather than toggling between two separate files to do so.

 

Now you'll just need to learn how to use ES3-style javascript to ingest and split up a basic CSV (basic assumes there's no semicolons between your semicolons, so you never have a cell that is completely in quotes for the purpose of containing a literal semicolon - that's where you'll need CSV parsing code that does more advanced parsing than basic javascript String.split() function. This will be a great way to learn scripting, so do a search on this forum and see hundreds of examples of csv-reading code. The good news is that the process of reading text data and splitting it up so that a javascript array of rows and cells is created is not complicated and can be done in just a few lines of code (for basic csvs that is).

 

Let's leave the csv part and do a little exploration into the interesting part: making Illustrator do things!
In the script you can have a statement like this:
var doc = app.activeDocument;

doc.pathItems.getByName("45678900").fillColor = doc.swatches.getByName("RGB Red").color;

 

If you really have a path with such a name and a swatch with such a name, it should apply the color to it.

Now you'll have to learn how to use variables to stick a replaceable string in place of those hard-coded strings.

 

var doc = app.activeDocument;

var pathName = "45678900";

var swatchName = "RGB Red";

doc.pathItems.getByName(pathName).fillColor = doc.swatches.getByName(swatchName).color;

 

Then you can chart out some pseudo code, let's start without column headers and use hard-coded indexes to create a simplistic foundation:

{{NAME INDEX}} = 0 Note: javascript arrays begin at 0, so the 1st column in the csv would be at index 0.

{{COLOR INDEX}} = 1 (pretend the 2nd column has the swatch name)
{{DOC}} - get the active Illustrator document. (What happens when you use the app.activeDocument command and there's no document?)
{{CSV FILE}} - get csv file reference in the script somehow.

{{CSV TEXT}} - read the csv file contents into a variable that contains all of its text.

{{CSV ROWS}} - make the script split the text via nextline characters to get an array of row text.

>> do this for every row:

  {{ROW CELLS}} - split each row string by semi-colons to get a collection of cells.

  {{PATH NAME}} - get the cell at index {{NAME INDEX}} in the array {{ROW CELLS}}.

  {{SWATCH NAME}} - get the cell at index {{COLOR INDEX}} in the array {{ROW CELLS}}.

  Apply the color to the path with the scripting command:
  {{DOC}}.pathItems.getByName({{PATH NAME}}).fillColor = {{DOC}}.swatches.getByName({{SWATCH NAME}}).color;

 

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Adobe Community Professional ,
Jan 30, 2021

Copy link to clipboard

Copied

If you want to learn, here is one of the many csv-threads on this forum, and this one appeared in the "related" box: https://community.adobe.com/t5/illustrator/coloring-map-using-data-file-csv/td-p/11041707?page=1

That's one actually useful feature on these now forums compared to the old forums, if it didn't exist before.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Community Beginner ,
Feb 02, 2021

Copy link to clipboard

Copied

Hey, I'm very thankful for your help!!

 

#target illustrator
function test(){
var layerName = "Ebene1";
layerName = prompt(" layerName ?",layerName);
function readSemicolonCSV(filePath){
var f = File(filePath);
if(!f.exists){
alert(f + " is not found.");
return false;
}
var str = "";
f.open("r");
str = f.read();
f.close();
return str;
}
function getCells(str){
var rows = str.split(/[\n\r]/g);
for(var i=0; i<rows.length; i++){
rows[i] = rows[i].split(/;/g);
};
return rows;
}
function applyColorSettingsRow(layerName,regionName, colorName){
var doc = app.activeDocument, thisRegionShape, thisColor;
try{
thisRegionShape = doc.layers[layerName].pageItems.getByName(regionName);
thisColor = doc.swatches.getByName(colorName);
if (thisRegionShape.typename == "CompoundPathItem") {
thisRegionShape.pathItems[0].fillColor = thisColor.color;
}
else {
thisRegionShape.fillColor = thisColor.color;
}
} catch(e) {
alert("Region: " + regionName + "\tColor: " + colorName + "\r" + e + "\n\n");
}
}
if(app.documents.length == 0){
alert("No open documents detected.");
return;
}
var csvFile = File.openDialog("Open CSV File", "*.csv");
if(!csvFile){
alert("No file chosen");
return;
}
var data = getCells(readSemicolonCSV(csvFile));
var doc = app.activeDocument;
for(var i=1; i<data.length; i++){
applyColorSettingsRow(layerName,data[i][0], data[i][1]);
};
};
test();

 

 

I "wrote" this code with the help of the thread you linked to.

 

I ran into trouble tho. I wanted to color it first, so I have a CSV spreadsheet with

 

regionNamecolorName
21100000testcolor

 

I have a swatch called "testcolor" in AI.

 

Now I get the script warning:

"

Region: 21100000  Color: testcolor 

Error: No such element

"

I did some troubleshooting and I have my area paths, which are supposed to be filled, inside a group, together with the stroke path. But even if I cut out the area path and put it directly under my layer "Ebene 1" it doesn't fill.

 

What did I do wrong?

 

Kind regards and thank you for your amazing help already!!

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Adobe Community Professional ,
Feb 02, 2021

Copy link to clipboard

Copied

Sure thing! Looks like you put it all together well. Your problem is because you actually don't have a path with a name like initially assumed, you have probably some named groups with some shapes inside them. So you will have to add in some code to handle groups similar to the lines

if (thisRegionShape.typename == "CompoundPathItem") {
thisRegionShape.pathItems[0].fillColor = thisColor.color;
}

The typename for groups is "GroupItem". 

Also when looking inside of a layer for pageItems, you get only the top-level pageItems, not the nested children. Therefore if you have your named group or path inside another group which does not have a name, it will not be recognized when you try to look for it because it's not in the top level.

You also mentioned layers, so we don't know if you aren't also having to deal with sub-layers, which would make this harder to troubleshoot as people sometimes refer to shapes and groups as 'layers'.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Community Beginner ,
Feb 03, 2021

Copy link to clipboard

Copied

Hey, I managed to fill it, I changed the CompoundPathItem line to:

if (thisRegionShape.typename == "GroupItem") {
thisRegionShape.pathItems[0].fillColor = thisColor.color;
}

 

But now it obviously didn't color my CompoundPaths anymore.

I tried it with adding it with && CompoundPathItem

 

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Community Beginner ,
Feb 03, 2021

Copy link to clipboard

Copied

if (thisRegionShape.typename == "GroupItem" && thisRegionShape.typename == "CompoundPathItem") {
thisRegionShape.pathItems[0].fillColor = thisColor.color;
}

like this. But it does nothing! Any suggestion?

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Adobe Community Professional ,
Feb 03, 2021

Copy link to clipboard

Copied

You can't have something that's a group that's also a compound path, so what you are missing is one small change: change your && and operator to || which is an 'or'. Now it will do what you want, which is to work on the item if it's a group or compound path. 

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Community Beginner ,
Feb 04, 2021

Copy link to clipboard

Copied

Hey, that doesn't seem to work, unfortunately. I guess it's because I have the CompoundPathItems inside my named group like this.

group structure.png

 

I tried it with adding it here:

 

if (thisRegionShape.typename == "GroupItem") {
thisRegionShape.pathItems[0].fillColor = thisColor.color;

thisRegionShape.compoundPathItems[0].fillColor =thisColor.color;
}

 

This didn't work either unfortunately and an && after the first fillColor line didn't work too.

 

What did I do wrong?

 

And can you help me with opacity? I can't seem to figure out how to even modify it with a script, there is no topic on this forum and also nothing about it in the scripting guide. I wonder if that is even possible? Or if I should just use "mock opacity", by creating different swatches for the different shades of my color 🙂

 

I'm super thankful for your help!

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Silly-V LATEST
Adobe Community Professional ,
Feb 04, 2021

Copy link to clipboard

Copied

Yes, it looks like you can have either paths or compound paths in groups. So let's repeat the conditional statement and take care of the contained item.

 

var itemToChange = thisRegionShape.pageItems[0]; // thisRegionShape is always a group.
if (itemToChange.typename == "CompoundPathItem") {
  itemToChange.pathItems[0].fillColor = thisColor.color;
} else if () {
  itemToChange.fillColor = thisColor.color;
}

 

 And to change the opacity you can simply do myPageItem.opacity = 50 to change it, that's the easiest part of the job.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Most Valuable Participant ,
Jan 30, 2021

Copy link to clipboard

Copied

Am I right that your data uses comma as decimal separator (e.g. three and one half is 3,50)?

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Community Beginner ,
Jan 30, 2021

Copy link to clipboard

Copied

You are right, I'm German, we do it that way, I didn't really think about it. Sorry, I can convert it easily, if that's a problem.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more