2

I'm trying to create a new command in LaTeX that creates a Google Map URL pointing to the coordinates associated with the place specified as a parameter in the called command (a sort of "table lookup" function). The places and the relative coordinates are stored inside a coords.CSV file, and must be read using the datatool package.

The Google Map URL should be structured like this one:

https://www.google.com/maps/?q=<LAT>,<LNG>

where <LAT> and <LNG> are the latitude and longitude coordinates loaded from the coords.CSV file, which is structured in this way:

Place,LAT,LNG
Test,42.0000,42.0000
...

This is how the command was defined:

\usepackage{datatool}
\newcommand{\coords}[1]{
    % Loads the CSV
    \DTLsetseparator{,}
    \DTLloaddb{coords}{doc/coords.csv}
        
    % Assigns the coordinates to the variables \LAT and \LNG, relative to specific place (the parameter #1)
    \def \LAT {\DTLfetch{coords}{Place}{#1}{LAT}}
    \def \LNG {\DTLfetch{coords}{Place}{#1}{LNG}}
    
    % Generates the URL pointing to Google Maps
    Place: \href{https://www.google.com/maps/?q=\LNG ,\LNG}{#1}
}

Finally, I use the new command in this way:

\coords{Test}

I've managed to correctly load the coordinates of the place called inside the command (in this case "Test"), but when I try to generate the URL, LaTeX gives me a lot of errors, most of which are ! Undefined control sequence. If I remove \LAT and \LNG from the line where the URL is generated (inside the command definition), I don't get any error, but of course the URL does not contain any coordinates, since they are stored inside the \LAT and \LNG variables.

Is there a way to properly generate the URL using the defined variables inside the \href command?

This is a test example:

\documentclass[a4paper,10pt]{article}

\usepackage{hyperref}           
\usepackage{datatool}

\newcommand{\coords}[1]{
     % Loads the CSV
    \DTLsetseparator{,}
    \DTLloaddb{coords}{coords.csv}
        
    % Assigns the coordinates to the variables \LAT and \LNG, relative to specific place (the parameter #1)
    \def \LAT {\DTLfetch{coords}{Place}{#1}{LAT}}
    \def \LNG {\DTLfetch{coords}{Place}{#1}{LNG}}
    
    % Generates the URL pointing to Google Maps
    Place: \href{https://www.google.com/maps/?q=\LAT ,\LNG}{#1}
}

\begin{document}

\coords{Test}

\end{document}
  • 1
    As always on the site please provide a full but minimal self contained example that others can copy and test as is. That makes it a lot easier to help. Note also that the filtered log from your editor (the image you show here) often also does not help and it has a tendency to leave out important details, so a full log of a minimal example is better. – daleif Dec 4 '20 at 9:30
  • I've provided the test example as you asked. However, I can't upload the full log since it's long 1477 lines – Trial4life Dec 4 '20 at 9:47
  • We don't have your data (hence the term self contained), please also provide sample data – daleif Dec 4 '20 at 10:17
  • I did provide the sample data at the top of the post. I write it again here: coords.csv: Place,LAT,LNG Test,42,42 I think it shouldn't be too difficult to recreate a csv file with just 2 lines. – Trial4life Dec 4 '20 at 10:23
  • My bad, did not see that, you know you can add it you the tex file using the filecontents* env (it takes a mandatory filename as its argument) – daleif Dec 4 '20 at 10:25
4

You can use the same trick as in my answer to retrieving substring of `\DTLfetch`

\begin{filecontents*}{\jobname.csv}
Place,LAT,LNG
Test,42.0000,42.0000
\end{filecontents*}

\documentclass{article}
\usepackage{datatool}
\usepackage{hyperref}

\DTLsetseparator{,}
\DTLloaddb{coords}{\jobname.csv}

\newcommand{\DTLfetchsave}[5]{% see https://tex.stackexchange.com/a/335489/4427
  \edtlgetrowforvalue{#2}{\dtlcolumnindex{#2}{#3}}{#4}%
  \dtlgetentryfromcurrentrow{\dtlcurrentvalue}{\dtlcolumnindex{#2}{#5}}%
  \let#1\dtlcurrentvalue
}

\newcommand{\coords}[1]{%
  \DTLfetchsave{\LAT}{coords}{Place}{#1}{LAT}%
  \DTLfetchsave{\LNG}{coords}{Place}{#1}{LNG}%
  % Generates the URL pointing to Google Maps
  Place: \href{https://www.google.com/maps/?q=\LAT,\LNG}{#1}%
}

\begin{document}

\coords{Test}

\end{document}

I used \jobname to avoid clobbering my files. You can use whatever file name you want for the database.

Load the database once, not everytime you call \coords.

  • Seems I overlook something subtle, thus I ask: Why \dtlgetentryfromcurrentrow{\dtlcurrentvalue}{\dtlcolumnindex{#2}{#5}}%...\let#1\dtlcurrentvalue instead of \dtlgetentryfromcurrentrow{#1}{\dtlcolumnindex{#2}{#5}}% ? – Ulrich Diez Dec 4 '20 at 12:53
  • @UlrichDiez I'm not sure the long command existed when I wrote the other answer; and “if it ain't broken, don't fix it”. – egreg Dec 4 '20 at 12:54
  • According to the datatool-manual you can use \dtlgetentryfromcurrentrow with whatsoever control-sequence - you are not bound to using \dtlcurrentvalue. "long command" - are you saying you don't wish to prevent the possibility of redefining \par? – Ulrich Diez Dec 4 '20 at 12:58
  • @UlrichDiez I was lazy and didn't want to copy \dtlgetentryfromcurrentrow and \dtlgetentryfromcurrentrow from your comment. ;-) – egreg Dec 4 '20 at 13:00
  • Thanks for the clarification. (Time to look in the mirror and do a facepalm to myself.) ;-) ... – Ulrich Diez Dec 4 '20 at 13:02
3

I suggest the following approach:

\documentclass[a4paper,10pt]{article}

% Let's create the file coords.csv - the directory ./doc  must exist 
% and writing-permission for that directory must be given!!!
% An already existing file won't be overwritten by the 
% filecontents*-environment (unless you provide the "overwrite"-option)
% and you will be informed about the fact that the file already
% exists via a message in the .log-file only. You won't get a 
% message on the terminal/console.
\begin{filecontents*}{doc/coords.csv}
Place,LAT,LNG
Test,42.0000,42.0000
\end{filecontents*}

\usepackage{hyperref}           
\usepackage{datatool}

\newcommand{\coords}[1]{%%%
    \begingroup
    % Load the CSV only if database "coords" doesn't already exist:
    \DTLifdbexists{coords}{}{%%%
      %\DTLsetseparator{,}% Comma is the default, so this probably is not needed.
      \DTLloaddb{coords}{doc/coords.csv}%%%
    }%%%
    % Assign the coordinates of the place whose name is denoted by the
    % parameter #1 to the macros \LAT and \LNG:
    \edtlgetrowforvalue{coords}{\dtlcolumnindex{coords}{Place}}{#1}%%%
    \dtlgetentryfromcurrentrow{\LAT}{\dtlcolumnindex{coords}{LAT}}%%%
    \dtlgetentryfromcurrentrow{\LNG}{\dtlcolumnindex{coords}{LNG}}%%%
    %%%
    % Use the name (denoted by #1) of the place as a hyperlink leading
    % to the corresponding URL of Google Maps:
    Place: \href{https://www.google.com/maps/?q=\LAT,\LNG}{#1}%%%
    \endgroup
}%%%

\begin{document}

\coords{Test}

\end{document}

I suggest this approach because with your code there are some issues:

Issue 1:

Your \coords-command produces unwanted space-tokens and \par-tokens:

I re-write it with comments indicating where these unwanted tokens come into being:

\newcommand{\coords}[1]{ %<- unwanted space-token yields horizontal space in horizontal mode
    % Loads the CSV
    \DTLsetseparator{,} %<- unwanted space-token yields horizontal space in horizontal mode
    \DTLloaddb{coords}{doc/coords.csv} %<- unwanted space-token yields horizontal space in horizontal mode
        %<- unwanted control word token \par
    % Assigns the coordinates to the variables \LAT and \LNG, relative to specific place (the parameter #1)
    \def \LAT {\DTLfetch{coords}{Place}{#1}{LAT}} %<- unwanted space-token yields horizontal space in horizontal mode
    \def \LNG {\DTLfetch{coords}{Place}{#1}{LNG}} %<- unwanted space-token yields horizontal space in horizontal mode
        %<- unwanted control word token \par                
    % Generates the URL pointing to Google Maps
    Place: \href{https://www.google.com/maps/?q=\LAT ,\LNG}{#1} %<- unwanted space-token yields horizontal space in horizontal mode
}

Issue 2:

The hyperref-manual says that tokens in the URL-argument of \href must be fully expandable.

Your commands \LAT and \LNG are not fully expandable because their definition contains the control word token \DTLfetch while the manual of the datatool-package clearly says that

\DTLfetch{students}{regnum}{\RegNum}{forename}
is equal to

\dtlgetrowforvalue{students}{\dtlcolumnindex{students}{regnum}}{\RegNum}%

\dtlgetentryfromcurrentrow{\dtlcurrentvalue}{\dtlcolumnindex{students}{forename}}% \dtlcurrentvalue

which indicates that \DTLfetch (and thus every macro whose expansion at some stage yields the token \DTLfetch) is not fully expandable as a macro \dtlcurrentvalue gets defined by \dtlgetentryfromcurrentrow.

Issue 3:

I doubt that it is necessary to load the database with each call to \coords.

Issue 4:

You use the command \DTLsetseparator for setting the separator for entries of a database to comma although this is the default. So this probably is obsolete.

  • I believe that the correct code should be on top of the answer, with the comments on the OP's code below. This way it would be clearer what to actually should be used. – egreg Dec 4 '20 at 13:04
  • @egreg You are right. Done. – Ulrich Diez Dec 4 '20 at 13:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.