Given a GIS raster with elevation data, How to design a topographic map in D3js ?
Is there any example of relief / topographic maps of cropped lands made using D3js ?
Not working: I explored the posibility of .tif > gdal_contour.py > .shp > topojson > d3js without success.
I use a makefile which contain all my commands. As my area of interest (France) is a crop of land areas, the gdal_contour.py approach generates broken isolines which does NOT create closed polygons. Also, the SVG end result fails. The only example of D3 topographic map I know is about Iceland, which, as an island, avoid this issue: cropping the country out of the world doesn't result in broken isolines.
nb: This project is part of the #Wikipedia #wikimaps project.
Topographic map now on D3js, with full makefile workflow ! See http://bl.ocks.org/hugolpz/6279966 (<= older code, compare to here on SO)
0. Requirements:
Geographic area: You may customize your geographic area of interest by editing one line within each of the 2 files : makefile#boxing and html#Geo-frame_borders with your own decimal coordinates fo W,N,E,S borders, something like:
var WNES = { "target": "France", "W": -5.3, "N":51.6, "E": 10.2, "S": 41.0 };
Software: make, curl, unzip, gdal (include ogr, gdal_calc.py, gdal_polygonize.py), nodejs, topojson. Helpful: touch. The makefile then manage to download the sources, process them, and output a single topojson file that the D3js code provided can use.
1. Save into folder name: /topo_map/topo.mk
# topojsoning:
final.json: levels.json
topojson --id-property none --simplify=0.5 -p name=elev -o final.json -- levels.json
# simplification approach to explore further. Feedbacks welcome.
# shp2jsoning:
levels.json: levels.shp
ogr2ogr -f GeoJSON -where "elev < 10000" levels.json levels.shp
# merge
levels.shp: level0001.shp level0050.shp level0100.shp level0200.shp level0500.shp level1000.shp level2000.shp level3000.shp level4000.shp level5000.shp
ogr2ogr levels.shp level0001.shp
ogr2ogr -update -append levels.shp level0050.shp
ogr2ogr -update -append levels.shp level0100.shp
ogr2ogr -update -append levels.shp level0200.shp
ogr2ogr -update -append levels.shp level0500.shp
ogr2ogr -update -append levels.shp level1000.shp
ogr2ogr -update -append levels.shp level2000.shp
ogr2ogr -update -append levels.shp level3000.shp
ogr2ogr -update -append levels.shp level4000.shp
ogr2ogr -update -append levels.shp level5000.shp
# Polygonize slices:
level0001.shp: level0001.tif
gdal_polygonize.py level0001.tif -f "ESRI Shapefile" level0001.shp level_0001 elev
level0050.shp: level0050.tif
gdal_polygonize.py level0050.tif -f "ESRI Shapefile" level0050.shp level_0050 elev
level0100.shp: level0100.tif
gdal_polygonize.py level0100.tif -f "ESRI Shapefile" level0100.shp level_0100 elev
level0200.shp: level0200.tif
gdal_polygonize.py level0200.tif -f "ESRI Shapefile" level0200.shp level_0200 elev
level0500.shp: level0500.tif
gdal_polygonize.py level0500.tif -f "ESRI Shapefile" level0500.shp level_0500 elev
level1000.shp: level1000.tif
gdal_polygonize.py level1000.tif -f "ESRI Shapefile" level1000.shp level_1000 elev
level2000.shp: level2000.tif
gdal_polygonize.py level2000.tif -f "ESRI Shapefile" level2000.shp level_2000 elev
level3000.shp: level3000.tif
gdal_polygonize.py level3000.tif -f "ESRI Shapefile" level3000.shp level_3000 elev
level4000.shp: level4000.tif
gdal_polygonize.py level4000.tif -f "ESRI Shapefile" level4000.shp level_4000 elev
level5000.shp: level5000.tif
gdal_polygonize.py level5000.tif -f "ESRI Shapefile" level5000.shp level_5000 elev
# Raster slicing:
level0001.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level0001.tif --calc="1*(A>0)" --NoDataValue=0
level0050.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level0050.tif --calc="50*(A>50)" --NoDataValue=0
level0100.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level0100.tif --calc="100*(A>100)" --NoDataValue=0
level0200.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level0200.tif --calc="200*(A>200)" --NoDataValue=0
level0500.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level0500.tif --calc="500*(A>500)" --NoDataValue=0
level1000.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level1000.tif --calc="1000*(A>1000)" --NoDataValue=0
level2000.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level2000.tif --calc="2000*(A>2000)" --NoDataValue=0
level3000.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level3000.tif --calc="3000*(A>3000)" --NoDataValue=0
level4000.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level4000.tif --calc="4000*(A>4000)" --NoDataValue=0
level5000.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level5000.tif --calc="5000*(A>5000)" --NoDataValue=0
# boxing:
crop.tif: ETOPO1_Ice_g_geotiff.tif
gdal_translate -projwin -5.3 41.0 10.2 51.6 ETOPO1_Ice_g_geotiff.tif crop.tif
# ulx uly lrx lry // W S E N
# unzip:
ETOPO1_Ice_g_geotiff.tif: ETOPO1.zip
unzip ETOPO1.zip
touch ETOPO1_Ice_g_geotiff.tif
# download:
ETOPO1.zip:
curl -o ETOPO1.zip 'http://www.ngdc.noaa.gov/mgg/global/relief/ETOPO1/data/ice_surface/grid_registered/georeferenced_tiff/ETOPO1_Ice_g_geotiff.zip'
clean:
rm `ls | grep -v 'zip' | grep -v 'Makefile'`
# Makefile v4b (#hugo_lz)
2. Create data by running the makfile :
cd ./topo_map
make -f ./topo.mk
3. D3js & HTML code with auto-focus:
<!-- language: html -->
<style>
svg { border: 5px solid #333; background-color: #C6ECFF;}
/* TOPO */
path.Topo_1 { fill:#ACD0A5; stroke: #0978AB; stroke-width: 1px; }
path.Topo_50 {fill: #94BF8B; }
path.Topo_100 {fill: #BDCC96; }
path.Topo_200 {fill: #E1E4B5; }
path.Topo_500 {fill: #DED6A3; }
path.Topo_1000 {fill:#CAB982 ; }
path.Topo_2000 {fill: #AA8753; }
path.Topo_3000 {fill: #BAAE9A; }
path.Topo_4000 {fill: #E0DED8 ; }
path.Topo_5000 {fill: #FFFFFF ; }
.download {
background: #333;
color: #FFF;
font-weight: 900;
border: 2px solid #B10000;
padding: 4px;
margin:4px;
}
</style>
<body>
<script src="http://code.jquery.com/jquery-2.0.2.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
// 1. -------------- SETTINGS ------------- //
// Geo-frame_borders in decimal ⁰: France
var WNES = { "W": -5.3, "N":51.6, "E": 10.2, "S": 41.0 };
// Geo values of interest :
var latCenter = (WNES.S + WNES.N)/2,
lonCenter = (WNES.W + WNES.E)/2,
geo_width = (WNES.E - WNES.W),
geo_height= (WNES.N - WNES.S);
// HTML expected frame dimensions
var width = 600,
height = width * (geo_height / geo_width);
// Projection: projection, reset scale and translate
var projection = d3.geo.equirectangular()
.scale(1)
.translate([0, 0]);
// SVG injection:
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// Path
var path = d3.geo.path()
.projection(projection)
.pointRadius(4);
// Data (getJSON: TopoJSON)
d3.json("final.json", showData);
// 2. ---------- FUNCTION ------------- //
function showData(error, fra) {
var Levels = topojson.feature(fra, fra.objects.levels);
// Focus area box compute for derive scale & translate.
// [[left, bottom], [right, top]] // E W N S
var b = path.bounds(Levels),
s = 1 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];
// Projection update
projection
.scale(s)
.translate(t);
//Append Topo polygons
svg.append("path")
.datum(Levels)
.attr("d", path)
svg.selectAll(".levels")
.data(topojson.feature(fra, fra.objects.levels).features)
.enter().append("path")
.attr("class", function(d) { return "Topo_" + d.properties.name; })
.attr("data-elev", function(d) { return d.properties.name; })
.attr("d", path)
}
</script>
<br />
<div>
<a class="download ac-icon-download" href="javascript:javascript: (function () { var e = document.createElement('script'); if (window.location.protocol === 'https:') { e.setAttribute('src', 'https://raw.github.com/NYTimes/svg-crowbar/gh-pages/svg-crowbar.js'); } else { e.setAttribute('src', 'http://nytimes.github.com/svg-crowbar/svg-crowbar.js'); } e.setAttribute('class', 'svg-crowbar'); document.body.appendChild(e); })();"><!--⤋--><big>⇩</big> Download</a> -- Works on Chrome. Feedback me for others web browsers ?
</div>
<br />
</body>
</html>
4. Result will be precisely such: (applied to your area of interest)
If you publish map(s) online please share up the link :)
Note: encouraging +1 welcome.
If anyone is looking for an update, here's the build code I got running as of today. Required me to manually download the .zip file and move it into the topo_map directory, and then a few changes (noted in bold):
# topojsoning (USE GEO2TOPO not TOPOJSON):
final.json: levels.json
geo2topo --id-property none --simplify=0.5 -p name=elev -o final.json -- levels.json
# simplification approach to explore further. Feedbacks welcome.
# shp2jsoning:
levels.json: levels.shp
ogr2ogr -f GeoJSON -where "elev < 10000" levels.json levels.shp
# merge
levels.shp: level0001.shp level0050.shp level0100.shp level0200.shp level0500.shp level1000.shp level2000.shp level3000.shp level4000.shp level5000.shp
ogr2ogr levels.shp level0001.shp
ogr2ogr -update -append levels.shp level0050.shp
ogr2ogr -update -append levels.shp level0100.shp
ogr2ogr -update -append levels.shp level0200.shp
ogr2ogr -update -append levels.shp level0500.shp
ogr2ogr -update -append levels.shp level1000.shp
ogr2ogr -update -append levels.shp level2000.shp
ogr2ogr -update -append levels.shp level3000.shp
ogr2ogr -update -append levels.shp level4000.shp
ogr2ogr -update -append levels.shp level5000.shp
# Polygonize slices:
level0001.shp: level0001.tif
gdal_polygonize.py level0001.tif -f "ESRI Shapefile" level0001.shp level_0001 elev
level0050.shp: level0050.tif
gdal_polygonize.py level0050.tif -f "ESRI Shapefile" level0050.shp level_0050 elev
level0100.shp: level0100.tif
gdal_polygonize.py level0100.tif -f "ESRI Shapefile" level0100.shp level_0100 elev
level0200.shp: level0200.tif
gdal_polygonize.py level0200.tif -f "ESRI Shapefile" level0200.shp level_0200 elev
level0500.shp: level0500.tif
gdal_polygonize.py level0500.tif -f "ESRI Shapefile" level0500.shp level_0500 elev
level1000.shp: level1000.tif
gdal_polygonize.py level1000.tif -f "ESRI Shapefile" level1000.shp level_1000 elev
level2000.shp: level2000.tif
gdal_polygonize.py level2000.tif -f "ESRI Shapefile" level2000.shp level_2000 elev
level3000.shp: level3000.tif
gdal_polygonize.py level3000.tif -f "ESRI Shapefile" level3000.shp level_3000 elev
level4000.shp: level4000.tif
gdal_polygonize.py level4000.tif -f "ESRI Shapefile" level4000.shp level_4000 elev
level5000.shp: level5000.tif
gdal_polygonize.py level5000.tif -f "ESRI Shapefile" level5000.shp level_5000 elev
# Raster slicing:
level0001.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level0001.tif --calc="1*(A>0)" --NoDataValue=0
level0050.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level0050.tif --calc="50*(A>50)" --NoDataValue=0
level0100.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level0100.tif --calc="100*(A>100)" --NoDataValue=0
level0200.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level0200.tif --calc="200*(A>200)" --NoDataValue=0
level0500.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level0500.tif --calc="500*(A>500)" --NoDataValue=0
level1000.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level1000.tif --calc="1000*(A>1000)" --NoDataValue=0
level2000.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level2000.tif --calc="2000*(A>2000)" --NoDataValue=0
level3000.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level3000.tif --calc="3000*(A>3000)" --NoDataValue=0
level4000.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level4000.tif --calc="4000*(A>4000)" --NoDataValue=0
level5000.tif: crop.tif
gdal_calc.py -A crop.tif --outfile=level5000.tif --calc="5000*(A>5000)" --NoDataValue=0
# boxing:
crop.tif: ETOPO1_Ice_g_geotiff.tif
gdal_translate -projwin -84.9 47.0 -69.9 33.7 ETOPO1_Ice_g_geotiff.tif crop.tif
# ulx uly lrx lry // W N E S <- Coordinate order
# unzip:
ETOPO1_Ice_g_geotiff.tif: ETOPO1.zip
unzip ETOPO1.zip
touch ETOPO1_Ice_g_geotiff.tif
# download:
#ETOPO1.zip:
# curl -o ETOPO1.zip 'http://www.ngdc.noaa.gov/mgg/global/relief/ETOPO1/data/ice_surface/grid_registered/georeferenced_tiff/ETOPO1_Ice_g_geotiff.zip'
clean:
rm `ls | grep -v 'zip' | grep -v 'Makefile'`
# Makefile v4b (#Lopez_lz)
Related
I want to export one certain page from a pdf document to an image and automatically fill the page number in the file name. When I run the following code:
gs \
-sDEVICE=jpeg \
-o outfile-%03.jpeg \
-dFirstPage=12 \
-dLastPage=12 \
wo.pdf
I get: outfile-001.jpeg instead of outfile-012.jpeg.
I've wrote a bash script for the job:
function extract_nth_page(){
printf -v j "outfile-%05g.png" $1
echo $j
gs -q -dNOPAUSE -sDEVICE=png16m -r600 -dFirstPage=$1 -dLastPage=$1 -sOutputFile=$j $2 -c quit
return 0
}
# Extracts page number 42 from myFile.pdf to outfile-00042.png
extract_nth_page 42 myFile.pdf
I am trying to write to a specific directory/folder on my drive when running an imagemagick bash script.
I have come so far that I can giev arguments to my script in order to use the argument values to create a new directory (folder) into where I want to output an imagemagick file. I manage to create a new directory using the passed in arguments to the mkdir command in my bash script. However, the script gets stuck at the end of the execution of the mkdir command and never moves on.
This results in my dir being created, but I never get the chance to write to a file inside my new dir. I can later find the created dir but there is nothing in it. I have tried two different ways to create my dir and both get stuck.
When I call the script in my terminal it does all the steps until it gets to the mkdir command and then my terminal freezes, like it is waiting for something to happen so it can move on - hitting ENTER multiple times doesn't help either ;) - and I don't get the feeling it is stuck in a loop.
I am running a mkdir command as a response to err msgs I received when trying to output to the output dir without having the output dir created before writing to it.
I'm running bash on OSX El Capitan and below are selected fragments of the script relevant to my problem.
#!/bin/bash
args=("$#")
employer=${args[2]}
position=${args[3]}
output_dir="${employer} - ${position}"
# mkdir -pv -m u=rwx "$output_dir"
[ -d "$output_dir" ] && echo "Output Directory Exists" || mkdir -pv -m u=rwx "$output_dir"
# imagemagick script where a new image file gets written to my new output_dir
# the final string is the output path of my new imagemagick image output
convert - $logo_path -gravity center -geometry 350x350+280+70 -composite -set
filename:dimensions '%wx%h'
"${output_dir}/LN-${employer}-${position}-%[filename:dimensions].png"
EDIT:
After the first comments and response I feel obliged to give you guys the full code, perhaps there is something for you there to find. =) Also, as I mention in my comment below, clearing up the shell scripting issues following ShellCheck's directions (as advised by #Cyrus in the comments) messed up the imagemagick commands and the output failed. Therefore I have reverted back to old code in order to correct the code step by step from there, while maintaining the desired output the old (and buggy, according to ShellCheck) code produced. See code below.
The changes I made while debugging with ShellCheck did not fix the main problem with the hanging mkdir command. I am still failing when creating a new dir inside my $folder_path, cause it never moves on to actually outputting the image with the last convert command.
Additional problems I ran into after debugging with ShellCheck was that $custpath doesn't work at all when I try to use it inside the convert commands, which is why I use the entire value of $custpath in the convert commands for now. I have tried to use $"{custpath[#]}" and $"{custpath[*]}" as ShellCheck suggests and none of them succeed in creating the output, because - guess what - using $custpath hangs the script.
What I am trying to do is to put the output image inside
$custpath$folder_path$output_dir folder.
Here is the entire script:
#!/bin/bash -x
# Version: ImageMagick 6.9.6-7 Q16 x86_64 2016-12-05, running on macOSX 10.11.6 El Capitan
# run the script in bash terminal like so:
# ./my_script.sh date1 date2 "employer" "position" "path to logo img"
# remember to chmod the file to be able to run it: chmod +x my_script.sh
# this script is partially debugged with http://www.shellcheck.net/
args=("$#")
echo
echo date1 = "${args[0]}"
echo date2 = "${args[1]}"
echo employer = "${args[2]}"
echo position = "${args[3]}"
echo logo_path = "${args[4]}"
custpath=($HOME/Dropbox/+\ B-folder/A\ Folder/Gfx/Logos/)
echo custpath = "${custpath[#]}"
date1=${args[0]}
date2=${args[1]}
employer=${args[2]}
position=${args[3]}
logo_path=${args[4]}
# find the folder in the logo path
# see this article for how to replace a pattern from the end
# http://www.thegeekstuff.com/2010/07/bash-string-manipulation/
folder_path=${logo_path/%\/*//}
echo folder_path = $folder_path
output_dir="${employer} - ${position}"
echo output_dir = $output_dir
echo
convert \( -draw 'circle 108.5,101.5 159.5,160' -stroke "rgb(241,142,0)" -fill "rgb(241,142,0)" -size 1022x798 canvas:white -bordercolor black -border 1x1 -fill black -stroke black -draw 'rectangle 1,721 1022,798' -fill white -stroke none -background none -gravity south -page +93-11.5 -font /Library/Fonts/RobotoCondensed-Light.ttf -pointsize 35.5 label:'A Logo' -flatten \) $HOME/Dropbox/+dev/coding_images/imagemagick/a-logo-neg.png -geometry 207x+386+6.5 -composite miff:canvas0
convert -size 125x150 -background none -gravity center -stroke none -fill white -interline-spacing -7 -pointsize 33 -font /Library/Fonts/Roboto-Bold.ttf label:"Last\ndate\n${date1}/${date2}" miff:- |
composite -gravity center -geometry -402-300 - canvas0 miff:- |
convert - -size 255x150 -background none -gravity west -stroke none -fill black -kerning 0.5 -font /Library/Fonts/RobotoCondensed-Regular.ttf label:"${employer}" -geometry +33-102 -composite miff:- |
convert - -size 486x320 -background none -gravity west -stroke none -fill black -kerning 0.25 -interline-spacing -5 -font /Library/Fonts/Roboto-Regular.ttf caption:"${position}" -geometry +32+87 -composite miff:- |
# convert - $HOME/Dropbox/+\ B-folder/A\ Folder/Gfx/Logos/$logo_path -gravity center -geometry 350x350+280+70 -composite -set filename:dimensions '%wx%h' "$HOME/Dropbox/+\ B-folder/A\ Folder/Gfx/Logos/${folder_path}LN-${employer}-${position}-${date1}_${date2}-%[filename:dimensions].png"
# mkdir -pv -m u=rwx "$output_dir"
# [ -d "$output_dir" ] && echo "Output Directory Exists" || mkdir -pv -m u=rwx "$output_dir"
# cd "$output_dir"
# below is the final version of the output path I want to have, if only the $custpath variable worked
# convert - $logo_path -gravity center -geometry 350x350+280+70 -composite -set
# filename:dimensions '%wx%h'
# "${custpath}/${folder_path}/${output_dir}/LN-${employer}-${position}-%[filename:dimensions].png"
convert - $HOME/Dropbox/+\ B-folder/A\ Folder/Gfx/Logos/"$logo_path" -gravity center -geometry 350x350+280+70 -composite -set filename:dimensions '%wx%h' $HOME/Dropbox/+\ B-folder/A\ Folder/Gfx/Logos/"$folder_path"LN-"${employer}"-"${position}"-"${date1}"_"${date2}"-'%[filename:dimensions]'.png
convert - $"{custpath[#]}"/"$logo_path" -gravity center -geometry 350x350+280+70 -composite -set filename:dimensions '%wx%h' $HOME/Dropbox/+\ B-folder/A\ Folder/Gfx/Logos/"$folder_path"LN-"${employer}"-"${position}"-"${date1}"_"${date2}"-'%[filename:dimensions]'.png
rm canvas0
# open $HOME/Dropbox/+\ B-folder/A\ Folder/Gfx/Logos/"$folder_path"LN-"${employer}"-"${position}"-"${date1}"_"${date2}"-%[filename:dimensions].png
open $HOME/Dropbox/+\ B-folder/A\ Folder/Gfx/Logos/"$folder_path"LN-"${employer}"-"${position}"-"${date1}"_"${date2}"-*.png
# remove output file when testing the result, remove this line when script is finished
# sleep 5
# rm LN-"${employer}"-"${position}"-"${date1}"_"${date2}"-*.png
Your command:
convert - ...
tries to read an image from its standard input. So, it will hang forever unless you provide an image on standard input:
cat someImage.jpg | yourScript arg1 arg2
or name an image afterwards:
convert someImage.jpg ...
Maybe $logo_path already is the name of your image, in which case you would need:
convert "$logo_path" ...
By the way, #Jdamian's suggestion is a good one, in concrete terms, it means change your first line to:
#!/bin/bash -x
I solved the problem with the hanging mkdir by moving it to ~/.bash_profile and using a customized command for mkdir like so:
mk () {
case "$1" in /*) :;; *) set -- "./$1";; esac
mkdir -p "$1" #&& cd "$1"
}
# call the socmed function like so
# socmed day month "employer" "position" "path to logo"
function socmed {
args=("$#")
echo
echo date1 = "${args[0]}"
echo date2 = "${args[1]}"
echo employer = "${args[2]}"
echo position = "${args[3]}"
echo logo_path = "${args[4]}"
employer=${args[2]}
position=${args[3]}
logo_path=${args[4]}
folder_path=${logo_path/%\/*//}
custpath="$HOME/Dropbox/+ B-folder/A Folder/Gfx/Logos/"
output_dir="${employer} - ${position}"
mk "$custpath"/"$folder_path"/"$output_dir"
echo custpath = "$custpath"
echo folder_path = "$folder_path"
echo output_dir = "$output_dir"
echo
./full-tw.sh "${args[0]}" "${args[1]}" "${args[2]}" "${args[3]}" "${args[4]}"
./full-insta.sh "${args[0]}" "${args[1]}" "${args[2]}" "${args[3]}" "${args[4]}"
./full-ln.sh "${args[0]}" "${args[1]}" "${args[2]}" "${args[3]}" "${args[4]}"
./full-fb.sh "${args[0]}" "${args[1]}" "${args[2]}" "${args[3]}" "${args[4]}"
}
It doesn't matter that I have to shove the problem upwards one level, cause my intention was to use the bash profile anyway in order to have many images be created at the same time. =)
The script now looks like:
#!/bin/bash +x
# Version: ImageMagick 6.9.6-7 Q16 x86_64 2016-12-05, running on macOSX 10.11.6 El Capitan
# run the script in bash terminal like so:
# ./full-ln.sh 12 12 "Employer" "Position" "path to logo"
# remember to chmod the file to be able to run it: chmod +x full-ln.sh
# this script is partially debugged with http://www.shellcheck.net/
#
# this script was improved with the help of the nice ppl at StackOverflow
# http://stackoverflow.com/questions/41531443/mkdir-stuck-when-running-bash-script-for-imagemagick?noredirect=1#comment70275303_41531443
args=("$#")
# echo
# echo date1 = "${args[0]}"
# echo date2 = "${args[1]}"
# echo employer = "${args[2]}"
# echo position = "${args[3]}"
# echo logo_path = "${args[4]}"
# the new way for $custpath
custpath="$HOME/Dropbox/+ B-folder/A Folder/Gfx/Logos/"
# echo custpath = "$custpath"
# the old way below
# custpath=$HOME/Dropbox/+\ B-folder/A\ Folder/Gfx/Logos/
# echo custpath = "${custpath[#]}"
date1=${args[0]}
date2=${args[1]}
employer=${args[2]}
position=${args[3]}
logo_path=${args[4]}
# find the folder in the logo path
# see this article for how to replace a pattern from the end
# http://www.thegeekstuff.com/2010/07/bash-string-manipulation/
folder_path=${logo_path/%\/*//}
# echo folder_path = "$folder_path"
output_dir="${employer} - ${position}"
# echo output_dir = "$output_dir"
# echo
convert \( -draw 'circle 108.5,101.5 159.5,160' -stroke "rgb(241,142,0)" -fill "rgb(241,142,0)" -size 1022x798 canvas:white -bordercolor black -border 1x1 -fill black -stroke black -draw 'rectangle 1,721 1022,798' -fill white -stroke none -background none -gravity south -page +93-11.5 -font /Library/Fonts/RobotoCondensed-Light.ttf -pointsize 35.5 label:'A Folder Karriär' -flatten \) $HOME/Dropbox/+dev/coding_images/imagemagick/ah-karriar-neg.png -geometry 207x+386+6.5 -composite miff:canvas0
convert -size 125x150 -background none -gravity center -stroke none -fill white -interline-spacing -7 -pointsize 33 -font /Library/Fonts/Roboto-Bold.ttf label:"Sista\nansökan\n${date1}/${date2}" miff:- |
composite -gravity center -geometry -402-300 - canvas0 miff:- |
convert - -size 255x150 -background none -gravity west -stroke none -fill black -kerning 0.5 -font /Library/Fonts/RobotoCondensed-Regular.ttf label:"${employer} söker" -geometry +33-102 -composite miff:- |
convert - -size 486x320 -background none -gravity west -stroke none -fill black -kerning 0.25 -interline-spacing -5 -font /Library/Fonts/Roboto-Regular.ttf caption:"${position}" -geometry +32+87 -composite miff:- |
# failed at solving the making of a dir inside this script, moved that to bash_profile
# mkdir -pv -m u=rwx "$custpath"/"$folder_path"/"$output_dir"
# [ -d "$output_dir" ] && echo "Output Directory Exists" || mkdir -pv -m u=rwx "$output_dir"
convert - "$custpath"/"$logo_path" -gravity center -geometry 350x350+280+70 -composite -set filename:dimensions '%wx%h' "$custpath/$folder_path"/"$output_dir"/LN-"${employer}"-"${position}"-"${date1}"_"${date2}"-'%[filename:dimensions]'.png
rm canvas0
echo "LinkedIn-image complete"
echo
# open "$custpath"/"$folder_path/$output_dir"/LN-"${employer}"-"${position}"-"${date1}"_"${date2}"-*.png
# remove output file when testing the result, remove this line when script is finished
# sleep 5
# rm "$custpath"/"$folder_path/$output_dir"/LN-"${employer}"-"${position}"-"${date1}"_"${date2}"-*.png
I'm trying to write quotes onto an image automatically. The script that I'm writing picks a random background and appends a quote on the top, and puts smaller on the bottom the quote from the person, with some padding.
The only issue, I'm not able to get the text to work properly. I'm trying to write centred text to a box on the top and then the caption of where it's from centred on text in the bottom, with some padding in between and on the sides. How can I achieve this properly?
Here is what I'm getting:
This is how I'd like to text to be arranged by filling these boxes centred: (note that I don't want the boxes drawn, just illustrating where the text ought to be)
My code:
#!/bin/bash
# Get the text
text="This is text that will occupy until a certain space. This will be long on here."
bottom="By Michael A. Leonetti"
echo $text
bgs="bgs"
image="${bgs}/$( ls "${bgs}" | sort -R | tail -$N | head -1 )"
echo "Using background image ${image}"
width=1024
height=768
usable_percent="0.95"
bottom_percent="0.1"
padding_percent="0.05"
usable_width=$( echo "${width}*${usable_percent}"|bc )
usable_height=$( echo "${height}*${usable_percent}"|bc )
bottom_height=$( echo "${bottom_percent}*${usable_height}"|bc )
padding_height=$( echo "${padding_percent}*${usable_height}"|bc )
text_width=$usable_width
text_height=$( echo "${usable_height}-${bottom_height}-${padding_height}"|bc )
convert \
\( \
-size "${text_width}x${text_height}" \
-background none \
-fill white \
-gravity north \
caption:"${text}" \
-size "${text_width}x${bottom_height}" \
-background none \
-fill white \
-gravity south \
caption:"${bottom}" \
\) \
-composite \
\( \
"${image}" \
-resize "${width}x${height}^" \
-gravity center \
-crop "${width}x${height}+0+0" \
-brightness-contrast -30x0 \
\) \
+swap \
-gravity south \
-composite \
+repage \
"${bottom}.png"
I usually see and use a first ogr2ogr comand to convert from .shp to .geoJSON. In a second command, I use topojson.js to convert from .geoJSON to .topoJSON format, with simplification of the precision, coordinates, arcs, and filtering to keep relevant metadata only. Example :
# DOWNLOAD: Data from http://gadm.org/
CRI_adm.zip:
curl -o CRI_adm.zip http://gadm.org/data/shp/CRI_adm.zip
CRI_adm0.shp: CRI_adm.zip
unzip CRI_adm.zip
touch CRI_adm0.shp
# PROCESS DATA: SIMLIFY, FILTER
costarica.json: CRI_adm0.shp
ogr2ogr -f GeoJSON costarica.json CRI_adm0.shp
# Require topojson: https://github.com/mbostock/topojson
# (this minifies/simplifies the data)
costarica_min_topo.json: costarica.json
topojson \
-p name=NAME \
-p name \
-q 1e4 \
-o costarica_min_topo.json \
costarica.json
But, since topojson.js can convert from .shp directly into .topoJSON, with simplification of the precision, coordinates, arcs, can we jump ogr2ogr and directly convert and filter with a single topojson.js command ? Such:
# PROCESS DATA: SIMLIFY, FILTER
topojson \
-p name=NAME \
-p name \
-q 1e4 \
-o costarica_min_topo.json \
CRI_adm0.shp
ogr2ogr has a -where parameter. You can use -where "name=NAME" to filter the features of the shp file
I have a hand of commands which each works perfectly in the console when isolated. To ease my work, I collected them into a makefile:
topojsoning: levels.json
topojson --id-property none -p name=elev -o final.json levels.json
geojsoning: contours.shp
ogr2ogr -f GeoJSON -where "elev < 10000" levels.json contours.shp
shaping: crop.tif
gdal_contour -a elev -fl -1000 -500 -200 -50 0 50 100 200 500 1000 2000 4000 6000 crop.tif contours.shp
boxing: ETOPO1_Ice_g_geotiff.tif
gdal_translate -projwin -005.48 051.30 10.00 041.00 ETOPO1_Ice_g_geotiff.tif crop.tif
# ulx uly lrx lry // W N E S // -005.48 051.30 10.00 041.00
unzip: ETOPO1.zip
unzip ETOPO1.zip
touch ETOPO1_Ice_g_geotiff.tif
download:
curl -o ETOPO1.zip 'http://www.ngdc.noaa.gov/mgg/global/relief/ETOPO1/data/ice_surface/grid_registered/georeferenced_tiff/ETOPO1_Ice_g_geotiff.zip'
clean:
rm `ls | grep -v 'zip' | grep -v 'Makefile' `
Yet, when It is within my makefile, I get the following error :
make: *** No rule to make target ? `levels.json', needed by `topojsoning'. Stop.
What does this error means ? How to make it works ? Did I made a small typo ?
There is the full fix:
# topojsoning:
final.json: levels.json
topojson --id-property none -p name=elev -o final.json levels.json
# geojsoning:
levels.json: contours.shp
ogr2ogr -f GeoJSON -where "elev < 10000" levels.json contours.shp
# shaping:
contours.shp: crop.tif
gdal_contour -a elev -fl -1000 -500 -200 -50 0 50 100 200 500 1000 2000 4000 6000 crop.tif contours.shp
# boxing:
crop.tif: ETOPO1_Ice_g_geotiff.tif
gdal_translate -projwin -005.48 051.30 10.00 041.00 ETOPO1_Ice_g_geotiff.tif crop.tif
# ulx uly lrx lry // W N E S // -005.48 051.30 10.00 041.00
# unzip:
ETOPO1_Ice_g_geotiff.tif: ETOPO1.zip
unzip ETOPO1.zip
touch ETOPO1_Ice_g_geotiff.tif
# download:
ETOPO1.zip:
curl -o ETOPO1.zip 'http://www.ngdc.noaa.gov/mgg/global/relief/ETOPO1/data/ice_surface/grid_registered/georeferenced_tiff/ETOPO1_Ice_g_geotiff.zip'
clean:
rm `ls | grep -v 'zip' | grep -v 'Makefile' `
I was misusing the makefile syntaxe. I was starting each step by a process name (i.e. topojsoning, bad) :
processName: sourcefile
command
should start with the target file (final.json, good):
targetfile: sourcefile
command