%% The LaTeX package mercatormap - version 1.3.1 (2026/06/03)
%% mercatormap.sty: geographic coordinates (Mercator projection) and map tiles for TikZ pictures
%%
%% -------------------------------------------------------------------------------------------
%% Copyright (c) 2020-2026 by Prof. Dr. Dr. Thomas F. Sturm <thomas dot sturm at unibw dot de>
%% -------------------------------------------------------------------------------------------
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public License, either version 1.3
%% of this license or (at your option) any later version.
%% The latest version of this license is in
%%   http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of LaTeX
%% version 2005/12/01 or later.
%%
%% This work has the LPPL maintenance status `author-maintained'.
%%
%% This work consists of all files listed in README
%%
\NeedsTeXFormat{LaTeX2e}[2026-06-01]
\ProvidesExplPackage{mercatormap}{2026/06/03}{1.3.1}
  {A Web Mercator projection package with map tile support}

\RequirePackage{ siunitx,graphicx,tikz }

\ExplSyntaxOff
% space character problem if within explSyntax
\usetikzlibrary{shadings}
\ExplSyntaxOn

%\debug_on:n {all}


\ProvideExpandableDocumentCommand \mrcpkgprefix {} {}

\NewDocumentCommand\mermapset       { m } { \keys_set:nn { mermap } {#1} }
\NewDocumentCommand\mermapsetsupply { m } { \keys_set:nn { mermap/supply } {#1} }
\NewDocumentCommand\mermapsetmarker { m } { \keys_set:nn { mermap/marker } {#1} }


%---- scratch variables --------------------------------------------------------

\box_new:N \l__mermap_tmpa_box

\clist_new:N \l__mermap_tmpa_clist

\dim_new:N \l__mermap_tmpa_dim
\dim_new:N \l__mermap_tmpb_dim

\fp_new:N \l__mermap_tmpa_fp
\fp_new:N \l__mermap_tmpb_fp

\tl_new:N \g__mermap_tmpa_tl
\tl_new:N \l__mermap_tmpa_tl
\tl_new:N \l__mermap_tmpb_tl

%---- constants ----------------------------------------------------------------

\fp_const:Nn \c__mermap_scale_radius_fp { 6371*pi/180 }
\fp_const:Nn \c__mermap_mean_radius_fp { 637100000cm }
\fp_const:Nn \c__mermap_mile_fp { 1.609344 }

\tl_const:Ne \c__mermap_argone_colon_argtwo_tl { #1 \c_colon_str #2 }

%---- map definition -----------------------------------------------------------

\dim_new:N \l__mermap_tile_size_dim

\fp_new:N \l__mermap_mapeast_fp
\fp_new:N \l__mermap_mapnorth_fp
\fp_new:N \l__mermap_mapsouth_fp
\fp_new:N \l__mermap_mapwest_fp
\fp_new:N \l__mermap_tex_height_fp
\fp_new:N \l__mermap_tex_width_fp
\fp_new:N \l__mermap_tile_northoffset_fp
\fp_new:N \l__mermap_tile_southoffset_fp
\fp_new:N \l__mermap_tile_westoffset_fp

\int_new:N \l__mermap_tile_xmax_int
\int_new:N \l__mermap_tile_xmin_int
\int_new:N \l__mermap_tile_ymax_int
\int_new:N \l__mermap_tile_ymin_int
\int_new:N \l__mermap_tile_zoom_int

\tl_new:N \l__mermap_attribution_print_tl
\tl_new:N \l__mermap_attribution_tl
\tl_new:N \l__mermap_pixel_height_tl
\tl_new:N \l__mermap_pixel_width_tl
\tl_new:N \l__mermap_tile_basename_tl
\tl_new:N \l__mermap_tile_resource_tl


\keys_define:nn { mermap/mapdef }
  {
    west        .fp_set:N  = \l__mermap_mapwest_fp,
    east        .fp_set:N  = \l__mermap_mapeast_fp,
    north       .fp_set:N  = \l__mermap_mapnorth_fp,
    south       .fp_set:N  = \l__mermap_mapsouth_fp,
    zoom        .int_set:N = \l__mermap_tile_zoom_int,
    xmin        .int_set:N = \l__mermap_tile_xmin_int,
    ymin        .int_set:N = \l__mermap_tile_ymin_int,
    xmax        .int_set:N = \l__mermap_tile_xmax_int,
    ymax        .int_set:N = \l__mermap_tile_ymax_int,
    pixelwidth  .tl_set:N  = \l__mermap_pixel_width_tl,
    pixelheight .tl_set:N  = \l__mermap_pixel_height_tl,
    westoffset  .fp_set:N  = \l__mermap_tile_westoffset_fp,
    northoffset .fp_set:N  = \l__mermap_tile_northoffset_fp,
    southoffset .fp_set:N  = \l__mermap_tile_southoffset_fp,
    basename    .tl_set:N  = \l__mermap_tile_basename_tl,
    resource    .tl_set:N  = \l__mermap_tile_resource_tl,
    attribution .tl_set:N  = \l__mermap_attribution_tl,
    attribution~print .tl_set:N = \l__mermap_attribution_print_tl,
    tile~size   .dim_set:N = \l__mermap_tile_size_dim,
  }


\NewDocumentCommand \mrcdefinemap { m }
  {
    \keys_set:nn { mermap/mapdef }
      {
        west        = 11,
        east        = 13,
        north       = 50,
        south       = 48,
        zoom        = 9,
        xmin        = 271,
        ymin        = 173,
        xmax        = 275,
        ymax        = 177,
        pixelwidth  = 100,
        pixelheight = 100,
        westoffset  = 0,
        northoffset = 0,
        southoffset = 0,
        basename    = tiles/tile,
        resource    = none,
        attribution =,
        attribution~print =,
        #1
      }
    \__mermap_reset:
  }



%---- coordinate system --------------------------------------------------------

\fp_new:N \l__mermap_cs_lat_fp
\fp_new:N \l__mermap_cs_lon_fp
\fp_new:N \l__mermap_cs_southreference_fp
\fp_new:N \l__mermap_cs_x_fp
\fp_new:N \l__mermap_cs_xfactor_fp
\fp_new:N \l__mermap_cs_y_fp
\fp_new:N \l__mermap_cs_yfactor_fp
\fp_new:N \l__mermap_result_fp
\fp_new:N \l__mermap_scale_denominator_fp
\fp_new:N \l__mermap_scale_fp
\fp_new:N \l__mermap_vic_dim_fp
\fp_new:N \l__mermap_vic_east_fp
\fp_new:N \l__mermap_vic_north_fp
\fp_new:N \l__mermap_vic_south_fp
\fp_new:N \l__mermap_vic_west_fp
\fp_new:N \l__mermap_vic_westeast_fp
\fp_new:N \l__mermap_westeast_fp

\int_new:N \l__mermap_tile_number_int


\keys_define:nn { mermap }
  {
    vicinity .fp_set:N = \l__mermap_vic_dim_fp
  }
\keys_set:nn { mermap } { vicinity=2cm }


\NewExpandableDocumentCommand \mrcmapwest     {} { \fp_to_decimal:N \l__mermap_mapwest_fp }
\NewExpandableDocumentCommand \mrcmapeast     {} { \fp_to_decimal:N \l__mermap_mapeast_fp }
\NewExpandableDocumentCommand \mrcmapnorth    {} { \fp_to_decimal:N \l__mermap_mapnorth_fp }
\NewExpandableDocumentCommand \mrcmapsouth    {} { \fp_to_decimal:N \l__mermap_mapsouth_fp }
\NewExpandableDocumentCommand \mrcpixelwidth  {} { \tl_use:N \l__mermap_pixel_width_tl }
\NewExpandableDocumentCommand \mrcpixelheight {} { \tl_use:N \l__mermap_pixel_height_tl }
\NewExpandableDocumentCommand \mrctexwidth    {} { \fp_to_dim:N \l__mermap_tex_width_fp }
\NewExpandableDocumentCommand \mrctexheight   {} { \fp_to_dim:N \l__mermap_tex_height_fp }
\NewExpandableDocumentCommand \mrcscale       {} { \fp_to_decimal:N \l__mermap_scale_fp }
\NewExpandableDocumentCommand \mrcmapscaledenominator {} { \fp_to_decimal:N \l__mermap_scale_denominator_fp }
\NewDocumentCommand \mrcmapattribution      {} { \tl_use:N \l__mermap_attribution_tl }
\NewDocumentCommand \mrcmapattributionprint {} { \tl_use:N \l__mermap_attribution_print_tl }


\cs_new_protected_nopar:Npn \__mermap_reset:
  {
    \int_set:Nn \l__mermap_tile_number_int { \fp_to_int:n { 2^ \l__mermap_tile_zoom_int }}
    \fp_set:Nn \l__mermap_westeast_fp    { \l__mermap_mapeast_fp - \l__mermap_mapwest_fp }
    \fp_set:Nn \l__mermap_cs_xfactor_fp  { \l__mermap_tile_size_dim * \l__mermap_tile_number_int /360 }
    \fp_set:Nn \l__mermap_cs_yfactor_fp  { \l__mermap_tile_size_dim * \l__mermap_tile_number_int /2pi }
    \fp_set:Nn \l__mermap_cs_southreference_fp { tand( \l__mermap_mapsouth_fp /2+45) }

    \__mermap_longitude_to_x:n { \l__mermap_mapwest_fp }
    \__mermap_x_to_longitude:n { \l__mermap_cs_x_fp - \l__mermap_vic_dim_fp }
    \fp_set_eq:NN \l__mermap_vic_west_fp \l__mermap_cs_lon_fp

    \__mermap_longitude_to_x:n { \l__mermap_mapeast_fp }
    \__mermap_x_to_longitude:n { \l__mermap_cs_x_fp + \l__mermap_vic_dim_fp }
    \fp_set_eq:NN \l__mermap_vic_east_fp \l__mermap_cs_lon_fp

    \__mermap_latitude_to_y:n { \l__mermap_mapsouth_fp }
    \__mermap_y_to_latitude:n { \l__mermap_cs_y_fp - \l__mermap_vic_dim_fp }
    \fp_set_eq:NN \l__mermap_vic_south_fp \l__mermap_cs_lat_fp

    \__mermap_latitude_to_y:n { \l__mermap_mapnorth_fp }
    \fp_set_eq:NN \l__mermap_tex_height_fp \l__mermap_cs_y_fp
    \__mermap_y_to_latitude:n { \l__mermap_cs_y_fp + \l__mermap_vic_dim_fp }
    \fp_set_eq:NN \l__mermap_vic_north_fp \l__mermap_cs_lat_fp

    \fp_set:Nn \l__mermap_tex_width_fp { \l__mermap_westeast_fp * \l__mermap_cs_xfactor_fp }

    \fp_set:Nn \l__mermap_vic_westeast_fp { \l__mermap_vic_east_fp - \l__mermap_vic_west_fp }

    \__mermap_mean_latitude:NN \l__mermap_mapsouth_fp \l__mermap_mapnorth_fp
    \fp_set:Nn \l__mermap_scale_denominator_fp
      {
        2* \c__mermap_mean_radius_fp *cosd( \l__mermap_result_fp )
        *pi / \l__mermap_tile_number_int / \l__mermap_tile_size_dim
      }
    \fp_set:Nn \l__mermap_scale_fp { \l__mermap_scale_denominator_fp / (1cm*100000) }

    \__mermap_if_inside_picture_environment:T
      {
        \node
          [
            rectangle,
            name       = mrcmap,
            at         = { (0,0) },
            above~right,
            line~width = 0mm,
            inner~sep  = 0mm,
            outer~sep  = 0mm,
            draw       = none,
            fill       = none,
            minimum~width  = \mrctexwidth,
            minimum~height = \mrctexheight
          ] {} ;
      }
  }


\cs_new_protected_nopar:Npn \__mermap_mean_latitude:NN #1#2
  {
    \fp_set:Nn \l__mermap_result_fp { 2*atand(sqrt(tand(#1/2+45)*tand(#2/2+45))) - 90 }
  }


\cs_new_protected_nopar:Npn \__mermap_longitude_to_x:n #1
  {
    \fp_set:Nn \l__mermap_tmpa_fp { #1 - \l__mermap_mapwest_fp }
    \fp_sub:Nn \l__mermap_tmpa_fp { 360*floor( \l__mermap_tmpa_fp /360) }
    \fp_set:Nn \l__mermap_tmpa_fp
      { \l__mermap_tmpa_fp > \l__mermap_westeast_fp
          ? (
              \l__mermap_tmpa_fp - \l__mermap_westeast_fp > 360 - \l__mermap_tmpa_fp
                ? \l__mermap_tmpa_fp - 360
                : \l__mermap_tmpa_fp
            )
          : \l__mermap_tmpa_fp
      }
    \fp_set:Nn \l__mermap_cs_x_fp { \l__mermap_tmpa_fp * \l__mermap_cs_xfactor_fp }
  }


\cs_new_protected_nopar:Npn \__mermap_latitude_to_y:n #1
  {
    \fp_set:Nn \l__mermap_cs_y_fp
      {
        ln( tand((#1)/2+45)/ \l__mermap_cs_southreference_fp ) * \l__mermap_cs_yfactor_fp
      }
  }


\cs_new_protected_nopar:Npn \__mermap_x_to_longitude:n #1
  {
    \fp_set:Nn \l__mermap_cs_lon_fp { \l__mermap_mapwest_fp + (#1) / \l__mermap_cs_xfactor_fp }
  }


\cs_new_protected_nopar:Npn \__mermap_y_to_latitude:n #1
  {
    \fp_set:Nn \l__mermap_cs_lat_fp
      {
        2*atand( \l__mermap_cs_southreference_fp *exp((#1)/ \l__mermap_cs_yfactor_fp ) ) - 90
      }
  }


\cs_new_protected_nopar:Npn \__mermap_pgfpoint:nn #1#2
  {
    \__mermap_latitude_to_y:n  {#1}
    \__mermap_longitude_to_x:n {#2}
    \pgfpoint{ \fp_to_dim:N \l__mermap_cs_x_fp }
             { \fp_to_dim:N \l__mermap_cs_y_fp }
  }


\exp_last_unbraced:NNV \cs_new_protected_nopar:Npn \__mermap_pgfpoint:w \c__mermap_argone_colon_argtwo_tl \q_stop
  {
    \__mermap_pgfpoint:nn {#1} {#2}
  }


\cs_new_protected_nopar:Npn \__mermap_pgfpoint:n #1
  {
    \__mermap_pgfpoint:w #1 \q_stop
  }


\NewDocumentCommand \mrcpgfpoint { mm }
  {
    \__mermap_pgfpoint:nn {#1} {#2}
  }


\keys_define:nn { mermap/cs }
  {
    latitude  .code:n = \__mermap_latitude_to_y:n  {#1},
    longitude .code:n = \__mermap_longitude_to_x:n {#1},
    lat       .code:n = \__mermap_latitude_to_y:n  {#1},
    lon       .code:n = \__mermap_longitude_to_x:n {#1},
  }


\tikzdeclarecoordinatesystem { __mrc }
  {
    \__mermap_pgfpoint:nn #1
  }


\tikzdeclarecoordinatesystem { mrcq }
  {
    \__mermap_pgfpoint:w #1 \q_stop
  }


\tikzdeclarecoordinatesystem { mrc }
  {
    \keys_set:nn { mermap/cs } { #1 }
    \pgfpoint{ \fp_to_dim:N \l__mermap_cs_x_fp }
             { \fp_to_dim:N \l__mermap_cs_y_fp }
  }


\prg_new_conditional:Npnn \__mermap_if_in_map:nn #1#2 { p, T, F, TF }
  {
    \fp_compare:nNnTF { #1 } > { \l__mermap_mapnorth_fp }
      {
        \prg_return_false:
      }
      {
        \fp_compare:nNnTF { #1 } < { \l__mermap_mapsouth_fp }
          {
            \prg_return_false:
          }
          {
            \fp_set:Nn \l__mermap_tmpa_fp { #2 - \l__mermap_mapwest_fp }
            \fp_sub:Nn \l__mermap_tmpa_fp { 360*floor( \l__mermap_tmpa_fp /360) }
            \fp_compare:nNnTF { \l__mermap_tmpa_fp } > { \l__mermap_westeast_fp }
              {
                \prg_return_false:
              }
              {
                \fp_set:Nn \l__mermap_cs_x_fp { \l__mermap_tmpa_fp * \l__mermap_cs_xfactor_fp }
                \__mermap_latitude_to_y:n {#1}
                \pgfnoderename{ mrclastpos } { mrcpos }
                \pgfcoordinate{ mrcpos }
                  {
                    \pgfpoint{ \fp_to_dim:N \l__mermap_cs_x_fp }
                             { \fp_to_dim:N \l__mermap_cs_y_fp }
                  }
                \prg_return_true:
              }
          }
      }
  }

\NewDocumentCommand \ifmrcinmap { mm }
  {
    \__mermap_if_in_map:nnTF {#1} {#2}
  }


\prg_new_conditional:Npnn \__mermap_if_in_vicinity:nn #1#2 { p, T, F, TF }
  {
    \fp_compare:nNnTF { #1 } > { \l__mermap_vic_north_fp }
      {
        \prg_return_false:
      }
      {
        \fp_compare:nNnTF { #1 } < { \l__mermap_vic_south_fp }
          {
            \prg_return_false:
          }
          {
            \fp_set:Nn \l__mermap_tmpa_fp { #2 - \l__mermap_vic_west_fp }
            \fp_sub:Nn \l__mermap_tmpa_fp { 360*floor( \l__mermap_tmpa_fp /360) }
            \fp_compare:nNnTF { \l__mermap_tmpa_fp } > { \l__mermap_vic_westeast_fp }
              {
                \prg_return_false:
              }
              {
                \fp_set:Nn \l__mermap_cs_x_fp
                  {
                    \l__mermap_tmpa_fp * \l__mermap_cs_xfactor_fp - \l__mermap_vic_dim_fp
                  }
                \__mermap_latitude_to_y:n {#1}
                \pgfnoderename{ mrclastpos } { mrcpos }
                \pgfcoordinate{ mrcpos }
                  {
                    \pgfpoint{ \fp_to_dim:N \l__mermap_cs_x_fp }
                             { \fp_to_dim:N \l__mermap_cs_y_fp }
                  }
                \prg_return_true:
              }
          }
      }
  }

\NewDocumentCommand \ifmrcinvicinity { mm }
  {
    \__mermap_if_in_vicinity:nnTF {#1} {#2}
  }




%---- named positions ----------------------------------------------------------


\cs_new_protected_nopar:Npn \__mermap_set_named_position:nnn #1#2#3
  {
    \cs_set_nopar:cpe { __mermap__lat__#1 } { \fp_to_decimal:n {#2} }
    \cs_set_nopar:cpe { __mermap__lon__#1 } { \fp_to_decimal:n {#3} }
  }


\NewDocumentCommand \mrcNPdef { mmm }
  {
    \__mermap_set_named_position:nnn {#1} {#2} {#3}
  }


\NewDocumentCommand \mrcNPfrompoint { mm }
  {
    \path (#2);
    \__mermap_y_to_latitude:n  { \pgf@y }
    \__mermap_x_to_longitude:n { \pgf@x }
    \__mermap_set_named_position:nnn {#1}
      { \fp_to_decimal:N \l__mermap_cs_lat_fp }
      { \fp_to_decimal:N \l__mermap_cs_lon_fp }
  }


\msg_new:nnnn { mercatormap } { np-undefined }
  { Named~point~'#1'~is~undefined. }
  {
    The~named~point~'#1'~is~not~known:~
    perhaps~it~is~spelled~incorrectly.
  }


\NewExpandableDocumentCommand \mrcNPlat { m }
  {
    \cs_if_exist_use:cF { __mermap__lat__#1 } { 0 }
  }


\NewExpandableDocumentCommand \mrcNPlon { m }
  {
    \cs_if_exist_use:cF { __mermap__lon__#1 } { 0 }
  }


\NewExpandableDocumentCommand \mrcNPcs { m }
  {
    __mrc~cs \c_colon_str { { \mrcNPlat{#1} } { \mrcNPlon{#1} } }
  }


\prg_new_conditional:Npnn \__mermap_if_NP_exists:n #1 { p, TF }
  {
    \cs_if_exist:cTF { __mermap__lat__#1 }
      {
        \cs_if_exist:cTF { __mermap__lon__#1 }
          {
            \prg_return_true:
          }
          {
            \prg_return_false:
          }
      }
      {
        \prg_return_false:
      }
  }


\NewDocumentCommand \ifmrcNPexists { m }
  {
    \__mermap_if_NP_exists:nTF { #1 }
  }


\prg_new_conditional:Npnn \__mermap_if_NP_in_map:n #1 { p, TF }
  {
    \__mermap_if_NP_exists:nTF { #1 }
      {
        \__mermap_if_in_map:nnTF { \mrcNPlat{#1} } { \mrcNPlon{#1} }
          {
            \prg_return_true:
          }
          {
            \prg_return_false:
          }
      }
      {
        \prg_return_false:
      }
  }


\NewDocumentCommand \ifmrcNPinmap { m }
  {
    \__mermap_if_NP_in_map:nTF { #1 }
  }


\prg_new_conditional:Npnn \__mermap_if_NP_in_vicinity:n #1 { p, TF }
  {
    \__mermap_if_NP_exists:nTF { #1 }
      {
        \__mermap_if_in_vicinity:nnTF { \mrcNPlat{#1} } { \mrcNPlon{#1} }
          {
            \prg_return_true:
          }
          {
            \prg_return_false:
          }
      }
      {
        \prg_return_false:
      }
  }


\NewDocumentCommand \ifmrcNPinvicinity { m }
  {
    \__mermap_if_NP_in_vicinity:nTF { #1 }
  }



%---- map tile download --------------------------------------------------------

\group_begin:
\char_set_catcode_space:n {`\ }% noqa: S204
\char_set_catcode_other:n {`\"}% noqa: S204

\tl_const:Nn \c__mermap_supply_call_boundaries_tl
  {%                                                           % noqa: S204
    \l__mermap_python_tl\c_space_tl boundaries                 % noqa: S204
      -u   "\str_use:N\l__mermap_supply_url_str"               % noqa: S204
      -at  "\str_use:N\l__mermap_supply_attribution_str"       % noqa: S204
      -atp "\str_use:N\l__mermap_supply_attribution_print_str" % noqa: S204
      -f   "\l__mermap_supply_basename_tl"                     % noqa: S204
      -z    \int_use:N\l__mermap_supply_zoom_int\c_space_tl    % noqa: S204
      -w    \fp_use:N\l__mermap_supply_west_fp\c_space_tl      % noqa: S204
      -e    \fp_use:N\l__mermap_supply_east_fp\c_space_tl      % noqa: S204
      -n    \fp_use:N\l__mermap_supply_north_fp\c_space_tl     % noqa: S204
      -s    \fp_use:N\l__mermap_supply_south_fp\c_space_tl     % noqa: S204
      -p    \l__mermap_supply_pixel_tl\c_space_tl              % noqa: S204
      -ts   \dim_use:N\l__mermap_tile_size_dim\c_space_tl      % noqa: S204
      -t    \l__mermap_supply_target_tl\c_space_tl             % noqa: S204
      -d   "\str_use:N\l__mermap_definition_id_str"%           % noqa: S204
  }

\tl_const:Nn \c__mermap_supply_call_reference_tl
  {%                                                           % noqa: S204
    \l__mermap_python_tl\c_space_tl reference                  % noqa: S204
      -u   "\str_use:N\l__mermap_supply_url_str"               % noqa: S204
      -at  "\str_use:N\l__mermap_supply_attribution_str"       % noqa: S204
      -atp "\str_use:N\l__mermap_supply_attribution_print_str" % noqa: S204
      -f   "\l__mermap_supply_basename_tl"                     % noqa: S204
      -mw   \fp_use:N\l__mermap_supply_width_fp\c_space_tl     % noqa: S204
      -mh   \fp_use:N\l__mermap_supply_height_fp\c_space_tl    % noqa: S204
      -z    \int_use:N\l__mermap_supply_zoom_int\c_space_tl    % noqa: S204
      -lat  \fp_use:N\l__mermap_supply_latitude_fp\c_space_tl  % noqa: S204
      -lon  \fp_use:N\l__mermap_supply_longitude_fp\c_space_tl % noqa: S204
      -a    \l__mermap_supply_align_tl\c_space_tl              % noqa: S204
      -p    \l__mermap_supply_pixel_tl\c_space_tl              % noqa: S204
      -ts   \dim_use:N\l__mermap_tile_size_dim\c_space_tl      % noqa: S204
      -t    \l__mermap_supply_target_tl\c_space_tl             % noqa: S204
      -d   "\str_use:N\l__mermap_definition_id_str"%           % noqa: S204
  }

\tl_const:Nn \c__mermap_supply_call_areafit_tl
  {%                                                           % noqa: S204
    \l__mermap_python_tl\c_space_tl areafit                    % noqa: S204
      -u   "\str_use:N\l__mermap_supply_url_str"               % noqa: S204
      -at  "\str_use:N\l__mermap_supply_attribution_str"       % noqa: S204
      -atp "\str_use:N\l__mermap_supply_attribution_print_str" % noqa: S204
      -f   "\l__mermap_supply_basename_tl"                     % noqa: S204
      -mw   \fp_use:N\l__mermap_supply_width_fp\c_space_tl     % noqa: S204
      -mh   \fp_use:N\l__mermap_supply_height_fp\c_space_tl    % noqa: S204
      -w    \fp_use:N\l__mermap_supply_west_fp\c_space_tl      % noqa: S204
      -e    \fp_use:N\l__mermap_supply_east_fp\c_space_tl      % noqa: S204
      -n    \fp_use:N\l__mermap_supply_north_fp\c_space_tl     % noqa: S204
      -s    \fp_use:N\l__mermap_supply_south_fp\c_space_tl     % noqa: S204
      -a    \l__mermap_supply_align_tl\c_space_tl              % noqa: S204
      -p    \l__mermap_supply_pixel_tl\c_space_tl              % noqa: S204
      -ts   \dim_use:N\l__mermap_tile_size_dim\c_space_tl      % noqa: S204
      -t    \l__mermap_supply_target_tl\c_space_tl             % noqa: S204
      -d   "\str_use:N\l__mermap_definition_id_str"%           % noqa: S204
  }

\group_end:


\bool_new:N \l__mermap_script_activated_bool
\bool_new:N \l__mermap_fail_on_missing_resource_bool

\fp_new:N \l__mermap_supply_east_fp
\fp_new:N \l__mermap_supply_height_fp
\fp_new:N \l__mermap_supply_latitude_fp
\fp_new:N \l__mermap_supply_longitude_fp
\fp_new:N \l__mermap_supply_north_fp
\fp_new:N \l__mermap_supply_south_fp
\fp_new:N \l__mermap_supply_west_fp
\fp_new:N \l__mermap_supply_width_fp

\int_new:N \g__mermap_automap_int
\int_new:N \l__mermap_supply_zoom_int

\iow_new:N \l__mermap_file_iow

\prop_new:N \g__mermap_apikey_prop
\prop_new_linked:N \g__mermap_definition_prop

\str_new:N \l__mermap_definition_id_str
\str_new:N \l__mermap_definition_postfix_str
\str_new:N \l__mermap_definition_prefix_str
\str_new:N \l__mermap_supply_attribution_print_str
\str_new:N \l__mermap_supply_attribution_str
\str_new:N \l__mermap_supply_url_str

\tl_new:N \l__mermap_last_mdfivesum_tl
\tl_new:N \l__mermap_mdfivesum_tl
\tl_new:N \l__mermap_python_tl
\tl_new:N \l__mermap_supply_align_tl
\tl_new:N \l__mermap_supply_basename_tl
\tl_new:N \l__mermap_supply_call_tl
\tl_new:N \l__mermap_supply_pixel_tl
\tl_new:N \l__mermap_supply_target_tl
\tl_new:N \l__mermap_supply_type_tl


\exp_last_unbraced:NNV \cs_new_nopar:Npn \__mermap_supply_position:w \c__mermap_argone_colon_argtwo_tl \q_stop
  {
    \fp_set:Nn \l__mermap_supply_latitude_fp {#1}
    \fp_set:Nn \l__mermap_supply_longitude_fp {#2}
  }


\msg_new:nnnn { mercatormap } { api-key-undefined }
  { Api~key~'#1'~is~undefined. }
  {
    You~have~to~set~ \token_to_str:N \mrcsetapikey
      \iow_char:N \{#1 \iow_char:N \}
      \iow_char:N \{your~key \iow_char:N \}~first.
  }

\cs_new_protected:Npn \__mermap_supply_url_with_api_key:nnn #1#2#3
  {
    \str_set:Nn \l__mermap_supply_url_str {#1}
    \prop_get:NnNF \g__mermap_apikey_prop {#2} \l__mermap_tmpa_tl
      {
        \msg_error:nne { mercatormap } { api-key-undefined } { #2 }
      }
    \str_put_right:NV \l__mermap_supply_url_str \l__mermap_tmpa_tl
    \str_put_right:Nn \l__mermap_supply_url_str {#3}
  }


\cs_new_protected:Npn \__mermap_supply_add_area_clist:N #1
  {
    \clist_map_inline:Nn #1
      {
        \cs_if_exist:cF { __mermap__lat__##1 }
          {
            \msg_error:nne { mercatormap } { np-undefined } { ##1 }
          }
        \fp_set:Nn \l__mermap_tmpa_fp { \mrcNPlat{##1} }
        \fp_compare:nNnT \l__mermap_tmpa_fp < \l__mermap_supply_south_fp
          { \fp_set_eq:NN \l__mermap_supply_south_fp \l__mermap_tmpa_fp }
        \fp_compare:nNnT \l__mermap_tmpa_fp > \l__mermap_supply_north_fp
          { \fp_set_eq:NN \l__mermap_supply_north_fp \l__mermap_tmpa_fp }
        \fp_set:Nn \l__mermap_tmpa_fp { \mrcNPlon{##1} }
        \fp_compare:nNnT \l__mermap_tmpa_fp < \l__mermap_supply_west_fp
          { \fp_set_eq:NN \l__mermap_supply_west_fp \l__mermap_tmpa_fp }
        \fp_compare:nNnT \l__mermap_tmpa_fp > \l__mermap_supply_east_fp
          { \fp_set_eq:NN \l__mermap_supply_east_fp \l__mermap_tmpa_fp }
      }
  }


\msg_new:nnn { mercatormap } { input-area-marker }
  { The~marker~input~file~'#1'~does~not~exist. }

\cs_new:Npn \__mermap_supply_add_area_marker_position:
  {
    \fp_compare:nNnT { \l__mermap_marker_latitude_fp } < { \l__mermap_supply_south_fp }
      { \fp_set_eq:NN \l__mermap_supply_south_fp \l__mermap_marker_latitude_fp }
    \fp_compare:nNnT { \l__mermap_marker_latitude_fp } > { \l__mermap_supply_north_fp }
      { \fp_set_eq:NN \l__mermap_supply_north_fp \l__mermap_marker_latitude_fp }
    \fp_compare:nNnT { \l__mermap_marker_longitude_fp } < { \l__mermap_supply_west_fp }
      { \fp_set_eq:NN \l__mermap_supply_west_fp \l__mermap_marker_longitude_fp }
    \fp_compare:nNnT { \l__mermap_marker_longitude_fp } > { \l__mermap_supply_east_fp }
      { \fp_set_eq:NN \l__mermap_supply_east_fp \l__mermap_marker_longitude_fp }
  }

\cs_new_protected:Npn \__mermap_supply_add_area_marker:n #1
  {
    \group_begin:
      \RenewDocumentCommand \mrcmarker { m }
        {
          \keys_set:nn { mermap/marker } { ##1 }
          \__mermap_supply_add_area_marker_position:
        }
      \file_if_exist_input:nF {#1}
        {
          \msg_error:nnn { mercatormap } { input-area-marker } { #1 }
        }
      \tl_gset:Ne \g__mermap_tmpa_tl
        {
          \exp_not:N \fp_set:Nn \exp_not:N \l__mermap_supply_west_fp  { \fp_to_decimal:N \l__mermap_supply_west_fp }
          \exp_not:N \fp_set:Nn \exp_not:N \l__mermap_supply_east_fp  { \fp_to_decimal:N \l__mermap_supply_east_fp }
          \exp_not:N \fp_set:Nn \exp_not:N \l__mermap_supply_south_fp { \fp_to_decimal:N \l__mermap_supply_south_fp }
          \exp_not:N \fp_set:Nn \exp_not:N \l__mermap_supply_north_fp { \fp_to_decimal:N \l__mermap_supply_north_fp }
        }
      \group_insert_after:N \g__mermap_tmpa_tl
    \group_end:
  }


\keys_define:nn { mermap/supply }
  {
    zoom            .int_set:N = \l__mermap_supply_zoom_int,
    west            .fp_set:N  = \l__mermap_supply_west_fp,
    east            .fp_set:N  = \l__mermap_supply_east_fp,
    north           .fp_set:N  = \l__mermap_supply_north_fp,
    south           .fp_set:N  = \l__mermap_supply_south_fp,
    latitude        .fp_set:N  = \l__mermap_supply_latitude_fp,
    longitude       .fp_set:N  = \l__mermap_supply_longitude_fp,
    position        .code:n    = { \__mermap_supply_position:w #1 \q_stop },
    named~position  .meta:nn   = { mermap/supply } { latitude = \mrcNPlat{#1}, longitude = \mrcNPlon{#1} },
    width           .fp_set:N  = \l__mermap_supply_width_fp,
    height          .fp_set:N  = \l__mermap_supply_height_fp,
    align           .tl_set:N  = \l__mermap_supply_align_tl,
    url             .code:n    = \str_set:Nn \l__mermap_supply_url_str {#1},
    url~with~api~key  .code:n  = { \__mermap_supply_url_with_api_key:nnn #1 },
    basename        .tl_set:N  = \l__mermap_supply_basename_tl,
    type            .tl_set:N  = \l__mermap_supply_type_tl,
    target          .tl_set:N  = \l__mermap_supply_target_tl,
    pixel           .tl_set:N  = \l__mermap_supply_pixel_tl,
    attribution     .code:n    =
      {
        \str_set:Nn \l__mermap_supply_attribution_str {#1}
        \tl_set_rescan:Nnn \l__mermap_attribution_tl {} {#1}
      },
    attribution~print .code:n =
      {
        \str_set:Nn \l__mermap_supply_attribution_print_str {#1}
        \tl_set_rescan:Nnn \l__mermap_attribution_print_tl {} {#1}
      },
    dpi .code:n =
      {
        \tl_set:Ne \l__mermap_supply_pixel_tl
          {
            \fp_to_int:n { round(#1 * \l__mermap_tile_size_dim / 1in) }
          }
      },
    tex~width .code:n =
      {
        \fp_set:Nn \l__mermap_supply_width_fp { #1 / \l__mermap_tile_size_dim }
      },
    tex~height .code:n =
      {
        \fp_set:Nn \l__mermap_supply_height_fp { #1 / \l__mermap_tile_size_dim }
      },
    source .code:n =
      {
        \exp_args:Nne \keys_set:nn { mermap/supply } { _source = #1 }
      },
    source .value_required:n = true,
    _source .choice:,
    area .code:n =
      {
        \clist_set:Nn \l__mermap_tmpa_clist { #1 }
        \fp_set:Nn \l__mermap_supply_west_fp { inf }
        \fp_set:Nn \l__mermap_supply_east_fp { -inf }
        \fp_set:Nn \l__mermap_supply_south_fp { 89 }
        \fp_set:Nn \l__mermap_supply_north_fp { -89 }
        \__mermap_supply_add_area_clist:N \l__mermap_tmpa_clist
      },
    area .value_required:n = true,
    add~area .code:n =
      {
        \clist_set:Nn \l__mermap_tmpa_clist { #1 }
        \__mermap_supply_add_area_clist:N \l__mermap_tmpa_clist
      },
    area~from~marker~input .code:n =
      {
        \fp_set:Nn \l__mermap_supply_west_fp { inf }
        \fp_set:Nn \l__mermap_supply_east_fp { -inf }
        \fp_set:Nn \l__mermap_supply_south_fp { 89 }
        \fp_set:Nn \l__mermap_supply_north_fp { -89 }
        \__mermap_supply_add_area_marker:n { #1 }
      },
    area~from~marker~input .value_required:n = true,
    add~area~from~marker~input .code:n =
      {
        \__mermap_supply_add_area_marker:n { #1 }
      },
    flex~reference~scale .code:n =
      {
        \__mermap_set_flex_scale:nn {#1} { \l__mermap_supply_latitude_fp }
      },
    flex~reference~scale .value_required:n = true,
    area~to~reference  .code:n =
      {
        \fp_set:Nn \l__mermap_supply_longitude_fp { ( \l__mermap_supply_west_fp + \l__mermap_supply_east_fp )/2 }
        \__mermap_mean_latitude:NN \l__mermap_supply_north_fp \l__mermap_supply_south_fp
        \fp_set_eq:NN \l__mermap_supply_latitude_fp \l__mermap_result_fp
      },
    area~to~reference .value_forbidden:n = true,
    flex~area~scale   .meta:nn = { mermap/supply } { area~to~reference, flex~reference~scale = {#1} },
    flex~area~scale   .value_required:n = true,
    flex~area~fit .code:n =
      {
        \fp_compare:nNnF \l__mermap_tile_size_dim = \l__mermap_flex_tile_size_dim
          {
            \fp_set:Nn \l__mermap_tmpa_fp { \l__mermap_tile_size_dim / \l__mermap_flex_tile_size_dim }
            \fp_set:Nn \l__mermap_supply_width_fp  { \l__mermap_supply_width_fp  * \l__mermap_tmpa_fp }
            \fp_set:Nn \l__mermap_supply_height_fp { \l__mermap_supply_height_fp * \l__mermap_tmpa_fp }
          }
        \fp_set:Nn \l__mermap_supply_width_fp  { \l__mermap_supply_width_fp  -(#1)/ \l__mermap_flex_tile_size_dim }
        \fp_set:Nn \l__mermap_supply_height_fp { \l__mermap_supply_height_fp -(#1)/ \l__mermap_flex_tile_size_dim }
        \fp_set:Nn \l__mermap_tmpa_fp
          {
            min
            (
              180* \l__mermap_supply_width_fp / ( \l__mermap_supply_east_fp - \l__mermap_supply_west_fp ),
              pi* \l__mermap_supply_height_fp
                / ln( tand( \l__mermap_supply_north_fp /2+45) / tand( \l__mermap_supply_south_fp /2 + 45) )
            )
          }
        \fp_set:Nn \l__mermap_tmpb_fp { ln( \l__mermap_tmpa_fp )/ln(2) }
        \fp_set:Nn \l__mermap_tmpb_fp { round( \l__mermap_tmpb_fp )/ \l__mermap_tmpb_fp }
        \fp_set:Nn \l__mermap_tmpb_fp { \l__mermap_tmpa_fp ^( \l__mermap_tmpb_fp - 1 + 0.0000001) }
        \dim_set:Nn \l__mermap_tile_size_dim { \fp_to_dim:n { \l__mermap_flex_tile_size_dim / \l__mermap_tmpb_fp }}
        \fp_set:Nn \l__mermap_supply_width_fp
          { \l__mermap_supply_width_fp * \l__mermap_tmpb_fp + (#1)/ \l__mermap_tile_size_dim }
        \fp_set:Nn \l__mermap_supply_height_fp
          { \l__mermap_supply_height_fp * \l__mermap_tmpb_fp + (#1)/ \l__mermap_tile_size_dim }
      },
    flex~area~fit .default:n = 0pt,
  }


\keys_set:nn { mermap/supply }
  {
    zoom      = 9,
    north     = 50,
    south     = 48,
    west      = 11,
    east      = 13,
    latitude  = 49,
    longitude = 12,
    width     = 4,
    height    = 4,
    align     = center,
    basename  = tiles/tile,
    url       =,
    type      = reference,
    pixel     = 256,
    target    = tiles,
    attribution       =,
    attribution~print =,
  }


\keys_define:nn { mermap }
  {
    definition~prefix .code:n            = \str_set:Nn \l__mermap_definition_prefix_str {#1},
    fail~on~missing~resource .bool_set:N = \l__mermap_fail_on_missing_resource_bool,
    python              .code:n = {
      \tl_set:Ne \l__mermap_python_tl { #1 ~maptiles.texpy }
      },
  }

\keys_set:nn { mermap }
  {
    definition~prefix = maps/,
    fail~on~missing~resource = true,
    python = python,
  }


\msg_new:nnn { mercatormap } { python-script }
  { Python~script~'maptiles.texpy'~is~created~for~map~tile~download. }


\NewDocumentCommand \mrcactivatescript {}
  {
    \msg_info:nn { mercatormap } { python-script }
    \file_input:n { \mrcpkgprefix mercatorpy.def }
    \bool_set_true:N \l__mermap_script_activated_bool
  }
\@onlypreamble\mrcactivatescript


\NewDocumentCommand \mrcsetapikey { mm }
  {
     \prop_gput:Nnn \g__mermap_apikey_prop {#1} {#2}
  }


\cs_new_protected:Npn \__mermap_create_definition_id:n #1
  {
    \tl_set:Ne \l__mermap_tmpa_tl {#1}
    \tl_trim_spaces:N \l__mermap_tmpa_tl
    \str_set:Ne \l__mermap_definition_postfix_str { \tl_to_str:N \l__mermap_tmpa_tl }
    \str_concat:NNN \l__mermap_definition_id_str
      \l__mermap_definition_prefix_str \l__mermap_definition_postfix_str
  }

\cs_new_protected:Npn \__mermap_map:nn #1#2
  {
    \tl_set:Ne \l__mermap_tmpa_tl {#2}
    \tl_trim_spaces:N \l__mermap_tmpa_tl
    \tl_if_empty:NT \l__mermap_tmpa_tl
      {
        \int_gincr:N \g__mermap_automap_int
        \tl_set:Ne \l__mermap_tmpa_tl
          {
            automap-
            \int_compare:nNnTF { \g__mermap_automap_int } < { 10 }
              { 000 }
              {
                \int_compare:nNnTF { \g__mermap_automap_int } < { 100 }
                { 00 }
                {
                  \int_compare:nNnT { \g__mermap_automap_int } < { 1000 }
                    { 0 }
                }
              }
            \int_use:N \g__mermap_automap_int
          }
      }
    \exp_args:NV \__mermap_create_definition_id:n \l__mermap_tmpa_tl
    \__mermap_supplymap:n {#1}
    \__mermap_applymap:
  }

\NewDocumentCommand \mrcmap { O {} m }
  {
    \__mermap_map:nn {#1} {#2}
  }


\msg_new:nnn { mercatormap } { definition-id-used }
  { Map~definition~'#1'~already~existing }


\cs_new_protected:Npn \__mermap_supplymap:n #1
  {
    \group_begin:
    \keys_set:nn { mermap/supply } { #1 }
    \prop_if_in:NVT \g__mermap_definition_prop \l__mermap_definition_id_str
      {
        \msg_warning:nne { mercatormap } { definition-id-used } { \str_use:N \l__mermap_definition_id_str }
      }
    \prop_gput:NVn \g__mermap_definition_prop \l__mermap_definition_id_str {}
    \bool_if:NT \l__mermap_script_activated_bool
    {
      \__mermap_supplymap_call:
    }
    \group_end:
  }

\NewDocumentCommand \mrcsupplymap { O {} m }
  {
    \__mermap_create_definition_id:n {#2}
    \__mermap_supplymap:n {#1}
  }


\msg_new:nnnn { mercatormap } { shell-escape }
  { You~must~invoke~LaTeX~with~the~--shell-escape~flag~to~produce~map~'#1'. }
  { The~--shell-escape~flag~is~needed,~because~a~Python~sript~has~to~be~called;~see~package~documentation. }

\msg_new:nnnn { mercatormap } { target-resource }
  { Target~and~produced~resource~are~different~for~map~'#1'. }
  { Your~target~was~' \l__mermap_supply_target_tl '.\\
    The~produced~resource~is~' \l__mermap_tile_resource_tl '.\\
    Check,~if~' \l__mermap_supply_target_tl '~is~available~via~' \str_use:N \l__mermap_supply_url_str ' \\
    or~if~the~zoom~value~is~too~large.
  }

\msg_new:nnnn { mercatormap } { definition-production }
  { Map~definition~file~not~produced~for~map~'#1'. }
  { **~The~failed~shell~command~was:\\
    \l__mermap_supply_call_tl\\
    **~Python~setup~correct?
  }

\cs_new_protected:Npn \__mermap_supplymap_call:
  {
    \exp_args:NNc \tl_set:Ne \l__mermap_supply_call_tl { c__mermap_supply_call_ \l__mermap_supply_type_tl _tl }
    \tl_set:Ne \l__mermap_mdfivesum_tl { \str_mdfive_hash:e { \l__mermap_supply_call_tl } }
    \file_if_exist:nTF { \l__mermap_definition_id_str .def }
      {
        \tl_clear:N \l__mermap_last_mdfivesum_tl
        \file_if_exist_input:n { \l__mermap_definition_id_str .md5 }
        \str_if_eq:VVF \l__mermap_mdfivesum_tl \l__mermap_last_mdfivesum_tl
          {
            \__mermap_supplymap_call_shell:
          }
      }
      {
        \__mermap_supplymap_call_shell:
      }
  }

\cs_new_protected:Npn \__mermap_supplymap_call_shell:
  {
    \sys_if_shell_unrestricted:TF
      {
        \file_if_exist:nT  { \l__mermap_definition_id_str .def }
          {
            \iow_open:Nn \l__mermap_file_iow { \l__mermap_definition_id_str .def }
            \iow_now:Ne  \l__mermap_file_iow { \c_backslash_str def \c_backslash_str mrcdefinemap {} \c_percent_str }
            \iow_close:N \l__mermap_file_iow
          }

        \sys_shell_now:e { \l__mermap_supply_call_tl }

        \file_if_exist:nTF { \l__mermap_definition_id_str .def }
          {
            \file_input:n { \l__mermap_definition_id_str .def }
            \str_if_eq:VVTF \l__mermap_supply_target_tl \l__mermap_tile_resource_tl
              {
                \__mermap_write_mdfive:n { \l__mermap_mdfivesum_tl }
              }
              {
                \__mermap_write_mdfive:n { false }
                \bool_if:NTF \l__mermap_fail_on_missing_resource_bool
                  {
                    \msg_error:nne { mercatormap } { target-resource } { \l__mermap_definition_id_str }
                  }
                  {
                    \msg_warning:nne { mercatormap } { target-resource } { \l__mermap_definition_id_str }
                  }
              }
          }
          {
            \msg_error:nne { mercatormap } { definition-production } { \l__mermap_definition_id_str }
          }
      }
      {
        \msg_error:nne { mercatormap } { shell-escape } { \l__mermap_definition_id_str }
      }
  }

\NewDocumentCommand \mermaplastfivesum { m }
{
  \tl_set:Ne \l__mermap_last_mdfivesum_tl { #1 }
}

\cs_new_protected:Npn \__mermap_write_mdfive:n #1
  {
    \iow_open:Nn \l__mermap_file_iow { \l__mermap_definition_id_str .md5 }
    \iow_now:Ne  \l__mermap_file_iow { \c_backslash_str mermaplastfivesum {#1} \c_percent_str }
    \iow_close:N \l__mermap_file_iow
  }



%---- map drawing --------------------------------------------------------------


\dim_new:N \l__mermap_flex_tile_size_dim
\dim_new:N \l__mermap_tx_dim
\dim_new:N \l__mermap_ty_dim

\int_new:N \l__mermap_tile_x_int
\int_new:N \l__mermap_tile_xmod_int
\int_new:N \l__mermap_tile_y_int

\tl_new:N \l__mermap_draw_map_tl
\tl_new:N \l__mermap_tikz_map_clip_tl
\tl_new:N \l__mermap_tile_filename_tl


\prg_new_conditional:Npnn \__mermap_if_inside_picture_environment: { p, TF, T }
  {
    \cs_if_exist:NTF \pgfpictureid
    {
      \prg_return_true:
    }
    {
      \prg_return_false:
    }
  }


\cs_new_protected_nopar:Npn \__mermap_set_flex_scale:nn  #1#2
  {
    \fp_set:Nn \l__mermap_tmpa_fp
      { 1+ln( \c__mermap_mean_radius_fp /#1*cosd(#2)*pi/ \l__mermap_flex_tile_size_dim )/ln(2) }
    \keys_set:nn { mermap } { flex~zoom = \l__mermap_tmpa_fp }
  }


\exp_last_unbraced:NNV \cs_new_protected_nopar:Npn \__mermap_set_flex_scale:w \c__mermap_argone_colon_argtwo_tl \q_stop
  {
    \__mermap_set_flex_scale:nn {#1} {#2}
  }


\exp_last_unbraced:NNV \cs_new_protected_nopar:Npn \__mermap_set_named_flex_scale:w
  \c__mermap_argone_colon_argtwo_tl \q_stop
  {
    \__mermap_set_flex_scale:nn {#1} { \mrcNPlat{#2} }
  }


\keys_define:nn { mermap }
  {
    tile~size      .dim_set:N = \l__mermap_tile_size_dim,
    flex~tile~size .dim_set:N = \l__mermap_flex_tile_size_dim,
    flex~zoom      .code:n =
      {
        \int_set:Nn \l__mermap_supply_zoom_int { \fp_to_int:n { round(#1) } }
        \dim_set:Nn \l__mermap_tile_size_dim
          { \fp_to_dim:n { \l__mermap_flex_tile_size_dim *2^(#1 - \l__mermap_supply_zoom_int ) } }
      },
    flex~scale        .code:n = { \__mermap_set_flex_scale:w #1 \q_stop },
    named~flex~scale  .code:n = { \__mermap_set_named_flex_scale:w #1 \q_stop },
    map~clip        .tl_set:N = \l__mermap_tikz_map_clip_tl,
    map~scope         .code:n = \tikzset{ mermap_scope_style/.style = {#1} },
    map~path          .code:n = \tikzset{ mermap_path_style/.style = {#1} },
    draw            .tl_set:N = \l__mermap_draw_map_tl,
  }


\keys_set:nn { mermap }
  {
    tile~size      = 32.512mm,
    flex~tile~size = 32.512mm,
    map~clip       = \mrcclipmap,
    draw           = auto,
    map~scope      = ,
    map~path       =
      {
        upper~left  = green!50,
        upper~right = green!25,
        lower~left  = green!50!black!50,
        lower~right = green!25
      },
  }


\NewDocumentCommand \mrcclipmap {}
  {
    \path[clip] (mrcmap.south~west) rectangle (mrcmap.north~east);
  }

\NewDocumentCommand \mrcboundmap {}
  {
    \path[use~as~bounding~box] (mrcmap.south~west) rectangle (mrcmap.north~east);
  }


\cs_new_nopar:Npn \__mermap_drawmap_path:
  {
    \begin{ scope }
      [
        xshift = \fp_to_dim:n { (0.5 - \l__mermap_tile_westoffset_fp )* \l__mermap_tile_size_dim },
        yshift = \fp_to_dim:n { (0.5 - \l__mermap_tile_southoffset_fp )* \l__mermap_tile_size_dim },
        mermap_scope_style
      ]
    \tl_use:N \l__mermap_tikz_map_clip_tl
    \path[mermap_path_style] (mrcmap.south~west) rectangle (mrcmap.north~east);
    \end{ scope }
  }


\cs_new_protected_nopar:Npn \__mermap_drawtile:
  {
    \tl_set:Ne \l__mermap_tile_filename_tl
      {
        \exp_not:o \l__mermap_tile_basename_tl
        _ \int_use:N \l__mermap_tile_zoom_int
        _ \int_use:N \l__mermap_tile_xmod_int
        _ \int_use:N \l__mermap_tile_y_int
        .png
      }
    \file_if_exist:nTF { \l__mermap_tile_filename_tl }
      {
        \node[line~width=0mm,inner~sep=0mm,outer~sep=0mm,draw=none,fill=none,rectangle]
          at ( \l__mermap_tx_dim, \l__mermap_ty_dim )
          {
            \includegraphics [ width = \l__mermap_tile_size_dim ] { \l__mermap_tile_filename_tl }
          } ;
      }
      {
        \path [ mermap_path_style ]
          ( \l__mermap_tx_dim, \l__mermap_ty_dim )
          ++ ( - \l__mermap_tile_size_dim /2, - \l__mermap_tile_size_dim /2)
          rectangle
          +( \l__mermap_tile_size_dim, \l__mermap_tile_size_dim );
      }
  }


\cs_new_protected_nopar:Npn \__mermap_drawmap_tiles:
  {
    \begin{ scope }
      [
        xshift = \fp_to_dim:n { (0.5 - \l__mermap_tile_westoffset_fp )* \l__mermap_tile_size_dim },
        yshift = \fp_to_dim:n { (0.5 - \l__mermap_tile_southoffset_fp )* \l__mermap_tile_size_dim },
        mermap_scope_style
      ]
    \tl_use:N \l__mermap_tikz_map_clip_tl
    \int_set_eq:NN \l__mermap_tile_x_int \l__mermap_tile_xmin_int
    \dim_set:Nn \l__mermap_tx_dim { 0pt }
    \int_until_do:nNnn { \l__mermap_tile_x_int } > { \l__mermap_tile_xmax_int }
      {
        \dim_set:Nn \l__mermap_ty_dim { 0pt }
        \int_set:Nn \l__mermap_tile_xmod_int
          {
            \int_mod:nn { \l__mermap_tile_x_int } { \l__mermap_tile_number_int }
          }
        \int_compare:nNnT { \l__mermap_tile_xmod_int } < { 0 }
          {
            \int_add:Nn \l__mermap_tile_xmod_int { \l__mermap_tile_number_int }
          }
        \int_set_eq:NN \l__mermap_tile_y_int \l__mermap_tile_ymax_int
        \int_until_do:nNnn { \l__mermap_tile_y_int } < { \l__mermap_tile_ymin_int }
        {
          \int_compare:nNnF { \l__mermap_tile_y_int } < { 0 }
            {
              \int_compare:nNnT { \l__mermap_tile_y_int } < { \l__mermap_tile_number_int }
              {
                \__mermap_drawtile:
              }
            }
          \dim_add:Nn \l__mermap_ty_dim { \l__mermap_tile_size_dim }
          \int_decr:N \l__mermap_tile_y_int
        }
        \dim_add:Nn \l__mermap_tx_dim \l__mermap_tile_size_dim
        \int_incr:N \l__mermap_tile_x_int
      }
    \end{ scope }
  }


\cs_new_nopar:Npn \__mermap_drawsinglemap:
  {
    \begin{ scope } [ mermap_scope_style ]
    \tl_use:N \l__mermap_tikz_map_clip_tl
    \node
      [
        above~right,line~width=0mm,inner~sep=0mm,outer~sep=0mm,draw=none,fill=none,rectangle
      ] at (0,0)
      {
        \includegraphics[width=\mrctexwidth,height=\mrctexheight] { \l__mermap_definition_id_str .png }
      } ;
    \end{ scope }
  }


\cs_new_nopar:Npn \__mermap_drawmap_mergedmap:
  {
    \str_if_eq:VnTF \l__mermap_tile_resource_tl { mergedmap }
      {
        \__mermap_drawsinglemap:
      }
      {
        \__mermap_drawmap_path:
      }
  }


\cs_new_nopar:Npn \__mermap_drawmap_wmsmap:
  {
    \str_if_eq:VnTF \l__mermap_tile_resource_tl { wmsmap }
      {
        \__mermap_drawsinglemap:
      }
      {
        \__mermap_drawmap_path:
      }
  }

\cs_new_nopar:Npn \__mermap_drawmap_auto:
  {
    \cs_if_exist:cTF { __mermap_drawmap_ \l__mermap_tile_resource_tl : }
      {
        \use:c { __mermap_drawmap_ \l__mermap_tile_resource_tl : }
      }
      {
        \str_if_eq:VnTF \l__mermap_tile_resource_tl { none }
          {
            \__mermap_drawmap_path:
          }
          {
            \msg_warning:nne { mercatormap } { draw-undefined } { \tl_use:N \l__mermap_tile_resource_tl }
          }
      }
  }

\msg_new:nnnn { mercatormap } { draw-undefined }
  { 'draw=#1'~is~undefined. }
  {
    The~option~value~'#1'~is~not~known~for~'draw':
    perhaps~it~is~spelled~incorrectly.
  }

\NewDocumentCommand \mrcdrawmap { o }
  {
    \group_begin:
    \IfNoValueF {#1}
      { \keys_set:nn { mermap } {#1} }
    \cs_if_exist:cTF { __mermap_drawmap_ \l__mermap_draw_map_tl : }
      {
        \use:c { __mermap_drawmap_ \l__mermap_draw_map_tl : }
      }
      {
        \msg_warning:nne { mercatormap } { draw-undefined } { \tl_use:N \l__mermap_draw_map_tl }
      }
    \group_end:
  }


\cs_new_protected_nopar:Npn \__mermap_applymap:
  {
    \file_input:n { \l__mermap_definition_id_str .def }
  }


\NewDocumentCommand \mrcapplymap { m }
  {
    \__mermap_create_definition_id:n {#1}
    \__mermap_applymap:
  }


\cs_new_protected_nopar:Npn \__mermap_drawinfo:
  {
    \begin{ scope }
      [
        every~node/.style = { fill=white,fill~opacity=0.8,text~opacity=1,font=\sffamily\footnotesize }
      ]
    \mrcclipmap
    \tl_set:Nn \l__mermap_tile_basename_tl { _dummy_ } % not existing file
    \mermapset{ map~path = { draw=white,double=red } }
    \tikzset{ mermap_path_style/.style = { draw=white,double=red } }
    \__mermap_drawmap_tiles:
    \node at (mrcmap.center)
      {
        \begin{ tabular } { ll }
        scale      & ca.~\mrcprettymapscale\\
        resolution & ca.~\mrcprettymapresolution\\
        \TeX\nobreakspace tile~size  & ca.~\mrcprettytilesize\\
        width      & ca.~\mrcprettymapwidth\\
        height     & ca.~\mrcprettymapheight\\
        zoom       & $z = \int_use:N \l__mermap_tile_zoom_int $\\
        horizontal & $x\in\{ \int_use:N \l__mermap_tile_xmin_int, \ldots, \int_use:N \l__mermap_tile_xmax_int \}$\\
        vertical   & $y\in\{ \int_use:N \l__mermap_tile_ymin_int, \ldots, \int_use:N \l__mermap_tile_ymax_int \}$
        \end{ tabular }
      } ;
    \node[above] at (mrcmap.south)
      {
        \mrcformlat[format~angle=decimal-4] { \mrcmapsouth },
        \SI[round-mode=figures,round-precision=6,detect-all]
          { \fp_to_decimal:n { \c__mermap_scale_radius_fp * ( \mrcmapeast - \mrcmapwest )*cosd( \mrcmapsouth ) }}
          { km }
      } ;
    \node[below] at (mrcmap.north)
      {
        \mrcformlat[format~angle=decimal-4] { \mrcmapnorth },
        \SI[round-mode=figures,round-precision=6,detect-all]
          { \fp_to_decimal:n { \c__mermap_scale_radius_fp * ( \mrcmapeast - \mrcmapwest )*cosd( \mrcmapnorth ) } }
          { km }
      } ;
    \node[rotate=90] at ([xshift=3mm]mrcmap.west)
      {
        \mrcformlon[format~angle=decimal-4] { \mrcmapwest },
        \SI[round-mode=figures,round-precision=6,detect-all]
          { \fp_to_decimal:n { \c__mermap_scale_radius_fp * ( \mrcmapnorth - \mrcmapsouth ) } }
          { km }
      } ;
    \node[rotate=90] at ([xshift=-3mm]mrcmap.east)
      {
        \mrcformlon[format~angle=decimal-4] { \mrcmapeast },
        \SI[round-mode=figures,round-precision=6,detect-all]
          { \fp_to_decimal:n { \c__mermap_scale_radius_fp * ( \mrcmapnorth - \mrcmapsouth ) } }
          { km }
      } ;
    \end{ scope }
  }

\NewDocumentCommand \mrcdrawinfo { } { \__mermap_drawinfo: }


%---- geodetic network ---------------------------------------------------------


\dim_new:N \l__mermap_network_distance_dim
\int_new:N \l__mermap_network_pieces_int
\tl_new:N  \l__mermap_network_font_tl

\keys_define:nn { mermap }
  {
    network~distance .dim_set:N = \l__mermap_network_distance_dim,
    network~pieces   .int_set:N = \l__mermap_network_pieces_int,
    network~font     .tl_set:N  = \l__mermap_network_font_tl,
  }

\keys_set:nn { mermap }
  {
    network~pieces   = 8,
    network~distance = 2cm,
    network~font     = \fontsize { 4pt } { 4pt } \sffamily,
  }

\cs_new_protected_nopar:Npn \__mermap_compute_network_step:nn #1#2
  {
    \fp_set:Nn \l__mermap_tmpa_fp
    {
      min ( \l__mermap_network_pieces_int, round((#1)/ \l__mermap_network_distance_dim ) )
    }
    \fp_set:Nn \l__mermap_tmpb_fp { (#2)/ \l__mermap_tmpa_fp }
    \fp_set:Nn \l__mermap_tmpa_fp { floor(ln( \l__mermap_tmpb_fp )/ln(10)) }
    \fp_compare:nNnTF { \l__mermap_tmpa_fp } < { 0 }
      {
        \fp_compare:nNnTF { \l__mermap_tmpa_fp } > { -5 }
          {
            \exp_args:Nne \keys_set:nn { mermap } { format~angle = decimal \fp_to_int:N \l__mermap_tmpa_fp }
          }
          {
            \keys_set:nn { mermap } { format~angle=decimal }
          }
      }
      {
        \keys_set:nn { mermap } { format~angle=decimal-0 }
      }
    \fp_set:Nn \l__mermap_tmpa_fp { 10^ \l__mermap_tmpa_fp }
    \fp_set:Nn \l__mermap_tmpb_fp { \l__mermap_tmpb_fp / \l__mermap_tmpa_fp }
    \fp_compare:nNnTF { abs( \l__mermap_tmpb_fp - 1) } < { abs( \l__mermap_tmpb_fp - 2 ) }
      {
        \fp_compare:nNnTF { abs( \l__mermap_tmpb_fp - 1 ) } < { abs( \l__mermap_tmpb_fp - 5 ) }
        {
          \fp_set:Nn \l__mermap_result_fp  { \l__mermap_tmpa_fp }
        }
        {
          \fp_set:Nn \l__mermap_result_fp { 5* \l__mermap_tmpa_fp }
        }
      }
      {
        \fp_compare:nNnTF { abs( \l__mermap_tmpb_fp - 2 ) } < { abs( \l__mermap_tmpb_fp - 5 ) }
        {
          \fp_set:Nn \l__mermap_result_fp { 2* \l__mermap_tmpa_fp }
        }
        {
          \fp_set:Nn \l__mermap_result_fp { 5* \l__mermap_tmpa_fp }
        }
      }
  }


\NewDocumentCommand \mrcdrawnetwork { o }
  {
    \begin{ scope }
      [
        every~node/.style =
          {
            inner~sep=0.5pt,black!50!gray,
            font = \l__mermap_network_font_tl,
            fill=white,opacity=0.3,text~opacity=1
          }
      ]
    \IfNoValueF {#1} { \keys_set:nn { mermap } {#1} }
    \tl_use:N \l__mermap_tikz_map_clip_tl
    \__mermap_compute_network_step:nn { \l__mermap_tex_height_fp } { \l__mermap_mapnorth_fp - \l__mermap_mapsouth_fp }
    \fp_set:Nn \l__mermap_tmpa_fp
      { ceil( \l__mermap_mapsouth_fp / \l__mermap_result_fp )* \l__mermap_result_fp }
    \fp_compare:nNnT { \l__mermap_tmpa_fp - \l__mermap_mapsouth_fp } < { 1e-10 }
      {
        \fp_add:Nn \l__mermap_tmpa_fp { \l__mermap_result_fp }
      }
    \fp_while_do:nNnn { \l__mermap_tmpa_fp } < { \l__mermap_mapnorth_fp }
      {
        \tl_set:Nn \l__mermap_tmpa_tl { \fp_use:N \l__mermap_tmpa_fp }
        \draw[gray,very~thin]
          ( mrc~cs \c_colon_str lat = \l__mermap_tmpa_tl, lon = \mrcmapwest )
          coordinate (mermap_temp)
          node
            [
              right=1mm,inner~sep=0.5pt,black!50!gray,
              font = \l__mermap_network_font_tl,
              fill=white,opacity=0.3,text~opacity=1,
            ]
            { \mrcformlat { \l__mermap_tmpa_tl } }
          -- (mermap_temp -| mrcmap.east)
          node
            [
              left=1mm,inner~sep=0.5pt,black!50!gray,
              font = \l__mermap_network_font_tl,
              fill=white,opacity=0.3,text~opacity=1,
            ]
            { \mrcformlat { \l__mermap_tmpa_tl } }
          ;
        \fp_add:Nn \l__mermap_tmpa_fp { \l__mermap_result_fp }
      }
    \__mermap_compute_network_step:nn { \l__mermap_tex_width_fp } { \l__mermap_mapeast_fp - \l__mermap_mapwest_fp }
    \fp_set:Nn \l__mermap_tmpa_fp
      { ceil( \l__mermap_mapwest_fp / \l__mermap_result_fp )* \l__mermap_result_fp }
    \fp_compare:nNnT { \l__mermap_tmpa_fp - \l__mermap_mapwest_fp } < { 1e-10 }
      {
        \fp_add:Nn \l__mermap_tmpa_fp { \l__mermap_result_fp }
      }
    \fp_while_do:nNnn { \l__mermap_tmpa_fp } < { \l__mermap_mapeast_fp }
      {
        \tl_set:Nn \l__mermap_tmpa_tl { \fp_use:N \l__mermap_tmpa_fp }
        \draw[gray,very~thin]
          ( mrc~cs \c_colon_str lon = \l__mermap_tmpa_tl, lat = \mrcmapsouth )
          coordinate ( mermap_temp )
          node
            [
              above=1mm,inner~sep=0.5pt,black!50!gray,
              font = \l__mermap_network_font_tl,
              fill=white,opacity=0.3,text~opacity=1,
            ]
            { \mrcformlon { \l__mermap_tmpa_tl } }
          -- (mermap_temp |- mrcmap.north)
          node
            [
              below=1mm,inner~sep=0.5pt,black!50!gray,
              font = \l__mermap_network_font_tl,
              fill=white,opacity=0.3,text~opacity=1,
            ]
            { \mrcformlon { \l__mermap_tmpa_tl } }
          ;
        \fp_add:Nn \l__mermap_tmpa_fp { \l__mermap_result_fp }
      }
    \end{ scope }
  }


%---- scaling and formatting ---------------------------------------------------

\tl_new:N \l__mermap_temp_tl

\keys_define:nn { mermap }
  {
    format~south .cs_set:Np = \__mermap_format_south:n #1,
    format~north .cs_set:Np = \__mermap_format_north:n #1,
    format~east .cs_set:Np  = \__mermap_format_east:n #1,
    format~west .cs_set:Np  = \__mermap_format_west:n #1,
    __format_angle .cs_set:Np = \__mermap_format_angle:n #1,
    format~angle .choice:,
    format~angle .value_required:n = true,
    format~angle / decimal   .meta:nn = { mermap } { __format_angle = \ang{##1}},
    format~angle / decimal-0 .meta:nn = { mermap }
      { __format_angle = { \ang[ round-mode=places,round-precision=0 ] {##1}}},
    format~angle / decimal-1 .meta:nn = { mermap }
      { __format_angle = { \ang[ round-mode=places,round-precision=1 ] {##1}}},
    format~angle / decimal-2 .meta:nn = { mermap }
      { __format_angle = { \ang[ round-mode=places,round-precision=2 ] {##1}}},
    format~angle / decimal-3 .meta:nn = { mermap }
      { __format_angle = { \ang[ round-mode=places,round-precision=3 ] {##1}}},
    format~angle / decimal-4 .meta:nn = { mermap }
      { __format_angle = { \ang[ round-mode=places,round-precision=4 ] {##1}}},
    format~angle / degree    .meta:nn = { mermap } { format~angle = decimal-0 },
    format~angle / minute    .meta:nn = { mermap } { __format_angle = \__mermap_format_angle_minute:n {##1}},
    format~angle / second    .meta:nn = { mermap } { __format_angle = \__mermap_format_angle_second:n {##1}},
    format~NEWS~absolute .meta:nn     = { mermap } {
      format~south = {##1}, format~north = {##1}, format~east = {##1}, format~west = {##1} },
    format~NEWS~numeric .meta:nn      = { mermap } {
      format~south = { $-$ ##1 }, format~north = {##1}, format~east = {##1}, format~west = { $-$ ##1 } },
  }


\keys_set:nn { mermap }
  {
    format~south = { #1\,S },
    format~north = { #1\,N },
    format~east  = { #1\,E },
    format~west  = { #1\,W },
    format~angle = decimal-4,
  }


\NewExpandableDocumentCommand \mrckmtotex { m }
  {
    \fp_to_dim:n { (#1) / \l__mermap_scale_fp }
  }

\NewExpandableDocumentCommand \mrcmiletotex { m }
  {
    \fp_to_dim:n { (#1) * \c__mermap_mile_fp / \l__mermap_scale_fp }
  }

\NewExpandableDocumentCommand \mrctextokm { m }
  {
    \fp_to_decimal:n { (#1) * \l__mermap_scale_fp }
  }

\NewExpandableDocumentCommand \mrctextomile { m }
  {
    \fp_to_decimal:n { (#1) / \c__mermap_mile_fp * \l__mermap_scale_fp }
  }

\NewDocumentCommand \mrcprettymapscale { }
  {
    1 \, \c_colon_str \,
    \num[round-mode=figures,round-precision=3,detect-all]
      { \fp_to_decimal:N \l__mermap_scale_denominator_fp }
  }


\cs_new_protected_nopar:Npn \__mermap_pretty_distance:n #1
  {
    \fp_set:Nn \l__mermap_tmpa_fp { #1 }
    \fp_compare:nNnTF { \l__mermap_tmpa_fp } < { 5 }
      {
        \SI[round-mode=figures,round-precision=3,detect-all]
          { \fp_to_decimal:n { 1000* \l__mermap_tmpa_fp } }
          { \meter }
      }
      {
        \SI[round-mode=figures,round-precision=3,detect-all]
          { \fp_to_decimal:N \l__mermap_tmpa_fp }
          { \kilo \meter }
      }
  }


\cs_new_protected_nopar:Npn \__mermap_pretty_length:n #1
  {
    \fp_set:Nn \l__mermap_tmpa_fp { (#1)* \l__mermap_scale_fp }
    \fp_compare:nNnTF { \l__mermap_tmpa_fp } < { 5 }
      {
        \SI[round-mode=figures,round-precision=3,detect-all]
          { \fp_to_decimal:n { 1000* \l__mermap_tmpa_fp } }
          { \meter }
      }
      {
        \SI[round-mode=figures,round-precision=3,detect-all]
          { \fp_to_decimal:N \l__mermap_tmpa_fp }
          { \kilo \meter }
      }
  }


\NewDocumentCommand \mrcprettymapwidth { }
  {
    \__mermap_pretty_length:n { \mrctexwidth }
  }

\NewDocumentCommand \mrcprettymapheight { }
  {
    \__mermap_pretty_length:n { \mrctexheight }
  }

\NewDocumentCommand \mrcprettymapresolution { }
  {
    \SI[round-mode=places,round-precision=0,detect-all]
      { \fp_to_decimal:n { 1in* \l__mermap_pixel_width_tl / \l__mermap_tex_width_fp } }
      { dpi }
  }

\NewDocumentCommand \mrcprettytilesize { }
  {
    \SI[round-mode=places,round-precision=3,detect-all]
      { \fp_to_decimal:n { \l__mermap_tile_size_dim /1mm } }
      { \milli \meter }
  }


\cs_new_protected_nopar:Npn \__mermap_format_angle_minute:n #1
  {
    \fp_set:Nn \l__mermap_tmpa_fp { floor(#1) }
    \fp_set:Nn \l__mermap_tmpb_fp { round((#1 - \l__mermap_tmpa_fp )*60) }
    \ang[add-arc-degree-zero,add-arc-minute-zero]
      {
        \fp_to_int:N \l__mermap_tmpa_fp ;
        \fp_to_int:N \l__mermap_tmpb_fp ;
      }
  }


\cs_new_protected_nopar:Npn \__mermap_format_angle_second:n #1
  {
    \fp_set:Nn \l__mermap_tmpa_fp { floor(#1) }
    \fp_set:Nn \l__mermap_tmpb_fp { floor((#1 - \l__mermap_tmpa_fp )*60) }
    \ang[add-arc-degree-zero,add-arc-minute-zero]
      {
        \fp_to_int:N \l__mermap_tmpa_fp ;
        \fp_to_int:N \l__mermap_tmpb_fp ;
        \fp_to_int:n { round(((#1 - \l__mermap_tmpa_fp )*60 - \l__mermap_tmpb_fp )*60) }
      }
  }

\NewDocumentCommand \mrcformlat { o m }
  {
    \group_begin:
    \IfNoValueF {#1}
      { \keys_set:nn { mermap } {#1} }
    \fp_set:Nn \l__mermap_tmpa_fp {#2}
    \fp_compare:nNnTF { \l__mermap_tmpa_fp } < { 0 }
      {
        \tl_set:Ne \l__mermap_temp_tl { \fp_to_decimal:n { - \l__mermap_tmpa_fp } }
        \__mermap_format_south:n { \__mermap_format_angle:n { \l__mermap_temp_tl } }
      }
      {
        \tl_set:Ne \l__mermap_temp_tl { \fp_to_decimal:N \l__mermap_tmpa_fp }
        \__mermap_format_north:n { \__mermap_format_angle:n { \l__mermap_temp_tl } }
      }
    \group_end:
  }

\NewDocumentCommand \mrcformlon { o m }
  {
    \group_begin:
    \IfNoValueF {#1}
      { \keys_set:nn { mermap } {#1} }
    \fp_set:Nn \l__mermap_tmpa_fp { #2-360*floor((#2+180)/360) }
    \fp_compare:nNnTF { \l__mermap_tmpa_fp } < { 0 }
      {
        \tl_set:Ne \l__mermap_temp_tl { \fp_to_decimal:n { - \l__mermap_tmpa_fp } }
        \__mermap_format_west:n { \__mermap_format_angle:n { \l__mermap_temp_tl } }
      }
      {
        \tl_set:Ne \l__mermap_temp_tl { \fp_to_decimal:N \l__mermap_tmpa_fp }
        \__mermap_format_east:n { \__mermap_format_angle:n { \l__mermap_temp_tl } }
      }
    \group_end:
  }


%---- scale bars ---------------------------------------------------------------

\bool_new:N \l__mermap_scalebar_double_bool
\bool_new:N \l__mermap_scalebar_transparent_bool
\dim_new:N  \l__mermap_scalebar_height_dim
\fp_new:N   \l__mermap_scalebar_width_fp
\int_new:N  \l__mermap_scalebar_partitions_int


\cs_new_protected_nopar:Npn \__mermap_scalebar_positioning:w #1#2#3#4#5;#6;#7 \q_stop
  {
    \tl_if_empty:nTF {#5}
    {
      \fp_set:Nn \l__mermap_tmpa_fp { 0 }
      \fp_set:Nn \l__mermap_tmpb_fp { 0 }
    }
    {
      \fp_set:Nn \l__mermap_tmpa_fp { #3*(#5) }
      \tl_if_empty:nTF {#6}
      {
        \fp_set:Nn \l__mermap_tmpb_fp { #4*(#5) }
      }
      {
        \fp_set:Nn \l__mermap_tmpb_fp { #4*(#6) }
      }
    }
    \tl_set:Ne \l__mermap_scalebar_at_tl
      {
        {
          (
            [
              xshift = \fp_to_dim:N \l__mermap_tmpa_fp,
              yshift = \fp_to_dim:N \l__mermap_tmpb_fp
            ]
            mrcmap.#1
          )
        }
      }
    \tl_set:Nn \l__mermap_scalebar_placement_tl {#2}
  }

\keys_define:nn { mermap/scalebar }
  {
    width-in-km    .fp_set:N   = \l__mermap_scalebar_width_fp,
    width-in-kilometer .meta:nn = { mermap/scalebar } { width-in-km = {#1} },
    width-in-meter .meta:nn    = { mermap/scalebar } { width-in-km = { (#1)/1000 } },
    width-in-mile  .meta:nn    = { mermap/scalebar } { width-in-km = { (#1)* \c__mermap_mile_fp } },
    width-in-yard  .meta:nn    = { mermap/scalebar } { width-in-km = { (#1)*0.0009144 } },
    partitions     .int_set:N  = \l__mermap_scalebar_partitions_int,
    height         .dim_set:N  = \l__mermap_scalebar_height_dim,
    at             .tl_set:N   = \l__mermap_scalebar_at_tl,
    placement      .tl_set:N   = \l__mermap_scalebar_placement_tl,
    major~style    .code:n     = \tikzset{ mrcscalebarmajor/.style = {#1} },
    minor~style    .code:n     = \tikzset{ mrcscalebarminor/.style = {#1} },
    double         .bool_set:N         = \l__mermap_scalebar_double_bool,
    single         .bool_set_inverse:N = \l__mermap_scalebar_double_bool,
    transparent    .bool_set:N         = \l__mermap_scalebar_transparent_bool,
    solid          .bool_set_inverse:N = \l__mermap_scalebar_transparent_bool,
    scale          .code:n     = { \fp_set:Nn \l__mermap_scale_fp { #1 / (1cm*100000) }},
    south-east-inside .code:n =
      { \__mermap_scalebar_positioning:w { south~east } { above~left } { -1 } { 1 } #1 ;; \q_stop },
    south-east-outside .code:n =
      { \__mermap_scalebar_positioning:w { south~east } { below~left } { -1 } { -1 } #1 ;; \q_stop },
    south-west-inside .code:n =
      { \__mermap_scalebar_positioning:w { south~west } { above~right } { 1 } { 1 } #1 ;; \q_stop },
    south-west-outside .code:n =
      { \__mermap_scalebar_positioning:w { south~west } { below~right } { 1 } { -1 } #1 ;; \q_stop },
    north-west-inside .code:n =
      { \__mermap_scalebar_positioning:w { north~west } { below~right } { 1 } { -1 } #1 ;; \q_stop },
    north-west-outside .code:n =
      { \__mermap_scalebar_positioning:w { north~west } { above~right } { 1 } { 1 } #1 ;; \q_stop },
    north-east-inside .code:n =
      { \__mermap_scalebar_positioning:w { north~east } { below~left } { -1 } { -1 } #1 ;; \q_stop },
    north-east-outside .code:n =
      { \__mermap_scalebar_positioning:w { north~east } { above~left } { -1 } { 1 } #1 ;; \q_stop },
  }

\keys_set:nn { mermap/scalebar }
  {
    width-in-km = 0,
    partitions  = 5,
    double      = true,
    transparent = true,
    height      = 2mm,
    at           = { (0,0) },
    placement    =,
    major~style  =,
    minor~style  =,
  }

\cs_new_protected_nopar:Npn \__mermap_drawscalebar:
  {
    \exp_last_unbraced:Ne \node
      {
        [
          rectangle,
          name           = mrcscalebar,
          at             = { \exp_not:V \l__mermap_scalebar_at_tl },
          \exp_not:V \l__mermap_scalebar_placement_tl,
          line~width     = 0mm,
          inner~sep      = 0mm,
          outer~sep      = 0mm,
          draw           = none,
          fill           = none,
          minimum~width  = \mrckmtotex { \l__mermap_scalebar_width_fp },
          minimum~height = \l__mermap_scalebar_height_dim
        ]
      } {} ;
    \begin{ scope } [ shift = (mrcscalebar.south~west) ]
    \__mermap_tikz_path_begin:n { fill=black, mrcscalebarmajor }
    \pgfseteorule
    \fp_set:Nn \l__mermap_tmpa_fp { \l__mermap_scalebar_width_fp / \l__mermap_scale_fp }
    \pgfpathrectangle
      { \pgfpoint { 0pt } { 0pt }}
      { \pgfpoint { \fp_to_dim:N \l__mermap_tmpa_fp } { \l__mermap_scalebar_height_dim }}
    \bool_if:NF \l__mermap_scalebar_transparent_bool
      {
        \__mermap_tikz_path_end:
        \__mermap_tikz_path_begin:n { fill=white, mrcscalebarminor }
        \fp_set:Nn \l__mermap_tmpa_fp { \l__mermap_scalebar_width_fp / \l__mermap_scale_fp }
      }
    \fp_set:Nn \l__mermap_tmpb_fp { \l__mermap_tmpa_fp / \l__mermap_scalebar_partitions_int }
    \bool_if:NTF \l__mermap_scalebar_double_bool
    {
      \dim_set:Nn \l__mermap_tmpb_dim { \l__mermap_scalebar_height_dim / 2 }
      \dim_set:Nn \l__mermap_tmpa_dim { 0.2pt - \l__mermap_tmpb_dim }
      \int_compare:nNnTF { \l__mermap_scalebar_partitions_int } = { 1 }
        {
          \pgfpathrectangle
            { \pgfpoint{ 0.2pt } { \l__mermap_tmpb_dim }}
            { \pgfpoint{ \fp_to_dim:n { \l__mermap_tmpb_fp -0.4pt }} { \l__mermap_tmpa_dim }}
        }
        {
          \pgfpathrectangle
            { \pgfpoint{ 0.2pt } { \l__mermap_tmpb_dim }}
            { \pgfpoint{ \fp_to_dim:n { \l__mermap_tmpb_fp -0.2pt }} { \l__mermap_tmpa_dim }}
        }
      \int_step_inline:nn { \l__mermap_scalebar_partitions_int - 1 }
        {
          \dim_set:Nn \l__mermap_tmpa_dim { - \l__mermap_tmpa_dim }
          \int_compare:nNnTF { ##1 +1 } = { \l__mermap_scalebar_partitions_int }
            {
              \pgfpathrectangle
                { \pgfpoint { \fp_to_dim:n { ##1* \l__mermap_tmpb_fp }} { \l__mermap_tmpb_dim }}
                { \pgfpoint { \fp_to_dim:n { \l__mermap_tmpb_fp -0.2pt }} { \l__mermap_tmpa_dim }}
            }
            {
              \pgfpathrectangle
                { \pgfpoint { \fp_to_dim:n { ##1* \l__mermap_tmpb_fp }} { \l__mermap_tmpb_dim }}
                { \pgfpoint { \fp_to_dim:N \l__mermap_tmpb_fp } { \l__mermap_tmpa_dim }}
            }
        }
    }
    {
      \dim_set:Nn \l__mermap_tmpa_dim { \l__mermap_scalebar_height_dim - 0.4pt }
      \int_step_inline:nnnn { 1 } { 2 } { \l__mermap_scalebar_partitions_int - 1 }
        {
          \int_compare:nNnTF { ##1 +1 } = { \l__mermap_scalebar_partitions_int }
            {
              \pgfpathrectangle
                { \pgfpoint{ \fp_to_dim:n { ##1* \l__mermap_tmpb_fp }} { 0.2pt }}
                { \pgfpoint{ \fp_to_dim:n { \l__mermap_tmpb_fp -0.2pt }} { \l__mermap_tmpa_dim }}
            }
            {
              \pgfpathrectangle
                { \pgfpoint{ \fp_to_dim:n { ##1* \l__mermap_tmpb_fp }} { 0.2pt }}
                { \pgfpoint{ \fp_to_dim:N \l__mermap_tmpb_fp } { \l__mermap_tmpa_dim }}
            }
        }
    }
    \__mermap_tikz_path_end:
    \end{ scope }
  }

\msg_new:nnnn { mercatormap } { scalebar-too-large }
  { The~width~of~the~scale~bar~is~too~large:~#1. }
  {
    You~should~provide~a~smaller~value~by~using~width-in-km~or~width-in-mile.
  }

\msg_new:nnnn { mercatormap } { scalebar-scale-unset }
  { The~map~scale~is~unknown. }
  {
    Use~\token_to_str:N \mrcdrawscalebar \ with~a~defined~map~or~set~'scale'~
    explicitely.
  }

\NewDocumentCommand \mrcdrawscalebar { o }
  {
    \group_begin:
    \IfNoValueF {#1} { \keys_set:nn { mermap/scalebar } {#1} }
    \fp_compare:nNnT { \l__mermap_scale_fp } = { 0 }
      {
        \msg_error:nn { mercatormap } { scalebar-scale-unset }
      }
    \fp_compare:nNnT { \l__mermap_scalebar_width_fp / \l__mermap_scale_fp } > { 575cm }
      {
        \msg_error:nne { mercatormap } { scalebar-too-large }
        { \fp_to_dim:n { \l__mermap_scalebar_width_fp / \l__mermap_scale_fp } }
      }
    \__mermap_drawscalebar:
    \group_end:
  }

%---- markers ------------------------------------------------------------------

\bool_new:N \l__mermap_marker_show_bool
\bool_new:N \l__mermap_marker_use_links_bool
\bool_new:N \l__mermap_marker_use_urls_bool

\fp_new:N \l__mermap_marker_angle_fp
\fp_new:N \l__mermap_marker_distance_fp
\fp_new:N \l__mermap_marker_inner_radius_fp
\fp_new:N \l__mermap_marker_latitude_fp
\fp_new:N \l__mermap_marker_longitude_fp
\fp_new:N \l__mermap_marker_radius_fp
\fp_new:N \l__mermap_marker_shift_fp

\tl_new:N \l__mermap_every_marker_first_tl
\tl_new:N \l__mermap_every_marker_last_tl
\tl_new:N \l__mermap_marker_alias_tl
\tl_new:N \l__mermap_marker_category_tl
\tl_new:N \l__mermap_marker_contents_tl
\tl_new:N \l__mermap_marker_font_tl
\tl_new:N \l_mermap_marker_generic_tl
\tl_new:N \l__mermap_marker_link_tl
\tl_new:N \l__mermap_marker_pictocontents_tl
\tl_new:N \l__mermap_marker_type_tl
\tl_new:N \l__mermap_marker_url_tl
\tl_new:N \l_mermap_marker_uuid_tl


\exp_last_unbraced:NNV \cs_new_nopar:Npn \__mermap_marker_position:w \c__mermap_argone_colon_argtwo_tl \q_stop
  {
    \fp_set:Nn \l__mermap_marker_latitude_fp {#1}
    \fp_set:Nn \l__mermap_marker_longitude_fp {#2}
  }


\keys_define:nn { mermap/marker }
  {
    latitude        .fp_set:N   = \l__mermap_marker_latitude_fp,
    lat             .fp_set:N   = \l__mermap_marker_latitude_fp,
    longitude       .fp_set:N   = \l__mermap_marker_longitude_fp,
    lon             .fp_set:N   = \l__mermap_marker_longitude_fp,
    position        .code:n     = { \__mermap_marker_position:w #1 \q_stop },
    named~position  .meta:nn    = { mermap/marker } { latitude=\mrcNPlat{#1}, longitude=\mrcNPlon{#1} },
    use~inside      .choice:    =,
    use~inside      .value_required:n = true,
    use~inside/map  .code:n     = { \cs_set_eq:NN \__mermap_if_marker_inside:nnTF \__mermap_if_in_map:nnTF },
    use~inside/vicinity .code:n = { \cs_set_eq:NN \__mermap_if_marker_inside:nnTF \__mermap_if_in_vicinity:nnTF },
    alias           .tl_set:N   = \l__mermap_marker_alias_tl,
    contents        .tl_set:N   = \l__mermap_marker_contents_tl,
    pictocontents   .tl_set:N   = \l__mermap_marker_pictocontents_tl,
    uuid            .tl_set:N   = \l_mermap_marker_uuid_tl,
    generic         .tl_set:N   = \l_mermap_marker_generic_tl,
    url             .tl_set:N   = \l__mermap_marker_url_tl,
    link            .tl_set:N   = \l__mermap_marker_link_tl,
    category        .tl_set:N   = \l__mermap_marker_category_tl,
    type            .tl_set:N   = \l__mermap_marker_type_tl,
    angle           .fp_set:N   = \l__mermap_marker_angle_fp,
    shift           .fp_set:N   = \l__mermap_marker_shift_fp,
    distance        .fp_set:N   = \l__mermap_marker_distance_fp,
    draw            .code:n     = \colorlet{ mrcmarkerdraw } {#1},
    fill            .code:n     = \colorlet{ mrcmarkerfill } {#1},
    text            .code:n     = \colorlet{ mrcmarkertext } {#1},
    font            .tl_set:N   = \l__mermap_marker_font_tl,
    radius          .fp_set:N   = \l__mermap_marker_radius_fp,
    inner~radius    .fp_set:N   = \l__mermap_marker_inner_radius_fp,
    path~style      .code:n     = {\tikzset{ mrcpathstyle/.style = {#1} }},
    node~style      .code:n     = {\tikzset{ mrcnodestyle/.style = {#1} }},
    show           .bool_set:N         = \l__mermap_marker_show_bool,
    hide           .bool_set_inverse:N = \l__mermap_marker_show_bool,
    show~category  .code:n      =
      { \str_if_eq:VnT \l__mermap_marker_category_tl {#1} { \bool_set_true:N \l__mermap_marker_show_bool } },
    show~all~but~category  .code:n =
      { \str_if_eq:VnF \l__mermap_marker_category_tl {#1} { \bool_set_true:N \l__mermap_marker_show_bool } },
    hide~category  .code:n      =
      { \str_if_eq:VnT \l__mermap_marker_category_tl {#1} { \bool_set_false:N \l__mermap_marker_show_bool } },
    hide~all~but~category  .code:n =
      { \str_if_eq:VnF \l__mermap_marker_category_tl {#1} { \bool_set_false:N \l__mermap_marker_show_bool } },
    use~links    .bool_set:N         = \l__mermap_marker_use_links_bool,
    ignore~links .bool_set_inverse:N = \l__mermap_marker_use_links_bool,
    use~urls     .bool_set:N         = \l__mermap_marker_use_urls_bool,
    ignore~urls  .bool_set_inverse:N = \l__mermap_marker_use_urls_bool,
    first~options  .tl_set:N = \l__mermap_every_marker_first_tl,
    last~options   .tl_set:N = \l__mermap_every_marker_last_tl,
    style        .choice:    =,
    style  .value_required:n = true,
  }


\keys_set:nn { mermap/marker }
  {
    latitude   = 12,
    longitude  = 49,
    use~inside = map,
    contents   =,
    alias      = noname,
    pictocontents =,
    angle      = 90,
    shift      = 0pt,
    distance   = 5mm,
    fill       = gray!20,
    draw       = gray,
    text       = black,
    radius     = 3mm,
    inner~radius = 2.25mm,
    path~style =,
    node~style =,
    font       = \sffamily\small,
    type       = classic,
    uuid       =,
    generic    =,
    url        =,
    link       =,
    category   =,
    show       = true,
    use~links  = true,
    use~urls   = true,
  }


\NewDocumentCommand \mrcmarkercontents      {} { \tl_use:N \l__mermap_marker_contents_tl }
\NewDocumentCommand \mrcmarkerpictocontents {} { \tl_use:N \l__mermap_marker_pictocontents_tl }
\NewDocumentCommand \mrcmarkergeneric       {} { \tl_use:N \l_mermap_marker_generic_tl }
\NewDocumentCommand \mrcmarkerfont          {} { \tl_use:N \l__mermap_marker_font_tl }

\NewExpandableDocumentCommand \mrcmarkerlatitude    {} { \fp_to_decimal:N \l__mermap_marker_latitude_fp }
\NewExpandableDocumentCommand \mrcmarkerlongitude   {} { \fp_to_decimal:N \l__mermap_marker_longitude_fp }
\NewExpandableDocumentCommand \mrcmarkeruuid        {} { \tl_use:N \l_mermap_marker_uuid_tl }
\NewExpandableDocumentCommand \mrcmarkercategory    {} { \tl_use:N \l__mermap_marker_category_tl }
\NewExpandableDocumentCommand \mrcmarkerangle       {} { \fp_to_decimal:N \l__mermap_marker_angle_fp }
\NewExpandableDocumentCommand \mrcmarkershift       {} { \fp_to_dim:N \l__mermap_marker_shift_fp }
\NewExpandableDocumentCommand \mrcmarkerdistance    {} { \fp_to_dim:N \l__mermap_marker_distance_fp }
\NewExpandableDocumentCommand \mrcmarkerradius      {} { \fp_to_dim:N \l__mermap_marker_radius_fp }
\NewExpandableDocumentCommand \mrcmarkerinnerradius {} { \fp_to_dim:N \l__mermap_marker_inner_radius_fp }


\NewDocumentCommand \mrcnewmarkerstyle { m +m }
  {
    \keys_define:nn { mermap/marker/style }
      {
        #1 .meta:nn = { mermap/marker } {#2}
      }
  }

\msg_new:nnnn { mercatormap } { markertype-undefined }
  { Marker~type~'#1'~is~unknown. }
  {
    I~guess~'type=#1'~for~setting~a~marker~type~contains~a~spelling~error.
  }


\cs_new_nopar:Npn \__mermap_hyper_path:n #1
  {
    \pgfpointanchor { path~picture~bounding~box } { south~west }
    \pgf@xb-\pgf@x
    \pgf@yb-\pgf@y
    \pgfpointanchor { path~picture~bounding~box } { north~east }
    \advance\pgf@xb\pgf@x
    \advance\pgf@yb\pgf@y
    \advance\pgf@x-1bp
    \advance\pgf@y-1bp
    \advance\pgf@xb-2bp
    \advance\pgf@yb-2bp
    \pgftext [ at = {\pgfqpoint{\pgf@x} {\pgf@y}}, right, top ]
    {
      \hypersetup { pdfborder = 0~0~0 }
      #1{\vrule height\pgf@yb depth0ptwidth0pt\vrule height0ptdepth0ptwidth\pgf@xb}
    }
  }


\cs_new_nopar:Npn \__mermap_hyper_path_insert:
  {
    \cs_if_exist:NT \hypersetup
      {
        \bool_if:NT \l__mermap_marker_use_urls_bool
        {
          \tl_if_empty:NF \l__mermap_marker_url_tl
            {
              \__mermap_hyper_path:n { \href { \l__mermap_marker_url_tl } }
            }
        }
        \bool_if:NT \l__mermap_marker_use_links_bool
        {
          \tl_if_empty:NF \l__mermap_marker_link_tl
            {
              \__mermap_hyper_path:n { \hyperlink { \l__mermap_marker_link_tl } }
            }
        }
      }
  }


\tikzset
  {
    mrchyperpath/.style =
      {
        path~picture = { \__mermap_hyper_path_insert: }
      }
  }

\NewDocumentCommand \mrcmarker { m }
  {
    \group_begin:
    \keys_set:nV { mermap/marker } \l__mermap_every_marker_first_tl
    \keys_set:nn { mermap/marker } { #1 }
    \keys_set:nV { mermap/marker } \l__mermap_every_marker_last_tl
    \__mermap_if_marker_inside:nnTF { \l__mermap_marker_latitude_fp } { \l__mermap_marker_longitude_fp }
    {
      \bool_if:NT \l__mermap_marker_show_bool
      {
        \pgfnodealias { \l__mermap_marker_alias_tl } { mrcpos }
        \begin{ scope } [ shift=(mrcpos) ]
        \cs_if_exist_use:cF { __mermap_drawmarker_ \l__mermap_marker_type_tl }
          {
            \msg_error:nne { mercatormap } { markertype-undefined } { \l__mermap_marker_type_tl }
          }
        \end{ scope }
      }
    }
    {}
    \group_end:
  }


\cs_new_protected:Npn \__mermap_new_marker_type:nn #1
  {
    \cs_new:cpn { __mermap_drawmarker_#1 }
  }


\NewDocumentCommand \mrcnewmarkertype { m }
  {
    \__mermap_new_marker_type:nn {#1}
  }


\__mermap_new_marker_type:nn { classic }
  {
    \node
      [
        circle,fill=black,inner~sep=0pt,minimum~width=4pt,
        pin =
          {
            [
              text=mrcmarkertext,
              font=\mrcmarkerfont,
              pin~distance=\mrcmarkerradius,
              pin~position=\mrcmarkerangle,
              mrcnodestyle,
              mrchyperpath
            ]
            \mrcmarkercontents
          }
      ] {} ;
  }

\__mermap_new_marker_type:nn { pin }
  {
    \tl_set:Ne \l__mermap_tmpa_tl { \fp_to_dim:N \l__mermap_marker_distance_fp }
    \path[fill=mrcmarkerfill,draw=none,fill~opacity=0.7]
      (0,0) -- (0.1, \l__mermap_tmpa_tl ) -- (-0.1, \l__mermap_tmpa_tl ) -- cycle;
    \node[fill=white,draw=none,text=mrcmarkertext,
      above,font=\mrcmarkerfont,inner~sep=0.5mm,align=center,
      line~width=0mm,xshift=\mrcmarkershift,
      fill~opacity=0.7,text~opacity=1,xshift=0mm,mrcnodestyle,mrchyperpath]
      (pin_node) at (0, \l__mermap_tmpa_tl ) {\mrcmarkercontents} ;
    \path[draw=mrcmarkerdraw,fill=none,line~join=round,mrcpathstyle]
      (0,0) -- (0.1, \l__mermap_tmpa_tl ) -- (pin_node.south~east) -- (pin_node.north~east)
      -- (pin_node.north~west) -- (pin_node.south~west)
      -- (-0.1, \l__mermap_tmpa_tl ) [line~join=bevel] --  cycle;
  }

\__mermap_new_marker_type:nn { pinflip }
  {
    \tl_set:Ne \l__mermap_tmpa_tl { \fp_to_dim:N \l__mermap_marker_distance_fp }
    \path[fill=mrcmarkerfill,draw=none,fill~opacity=0.7]
      (0,0) -- (-0.1,- \l__mermap_tmpa_tl ) -- (0.1,- \l__mermap_tmpa_tl ) -- cycle;
    \node[fill=white,draw=none,text=mrcmarkertext,
      below,font=\mrcmarkerfont,inner~sep=0.5mm,align=center,
      line~width=0mm,xshift=\mrcmarkershift,
      fill~opacity=0.7,text~opacity=1,xshift=0mm,mrcnodestyle,mrchyperpath]
      (pin_node) at (0,- \l__mermap_tmpa_tl ) {\mrcmarkercontents} ;
    \path[draw=mrcmarkerdraw,fill=none,line~join=round,mrcpathstyle]
      (0,0) -- (-0.1,- \l__mermap_tmpa_tl ) -- (pin_node.north~west) -- (pin_node.south~west)
      -- (pin_node.south~east) -- (pin_node.north~east)
      -- (0.1,- \l__mermap_tmpa_tl )  [line~join=bevel] -- cycle;
  }


\cs_new_protected_nopar:Npn \__mermap_tikz_drop_path:n #1
  {
    \tl_set:Nn \l__mermap_tmpa_tl { \fp_to_dim:N \l__mermap_marker_radius_fp }
    \tl_set:Nn \l__mermap_tmpb_tl { \fp_to_dim:n { \l__mermap_marker_radius_fp *0.552 } }
    \__mermap_tikz_path_begin:n
    {
      line~join=bevel,draw=mrcmarkerdraw,fill=mrcmarkerfill,mrcpathstyle,mrchyperpath
    }
    \pgfpathmoveto
      {
        \pgfpoint { 0pt } { \fp_to_dim:n { -2* \l__mermap_marker_radius_fp - \l__mermap_marker_shift_fp } }
      }
    \pgfpathcurveto
      { \pgfpoint { \l__mermap_tmpb_tl } { - \l__mermap_tmpa_tl }}
      { \pgfpoint { \l__mermap_tmpa_tl } { - \l__mermap_tmpb_tl }}
      { \pgfpoint { \l__mermap_tmpa_tl } { 0pt }}
    \pgfpathcurveto
      { \pgfpoint { \l__mermap_tmpa_tl } { \l__mermap_tmpb_tl }}
      { \pgfpoint { \l__mermap_tmpb_tl } { \l__mermap_tmpa_tl }}
      { \pgfpoint { 0pt } { \l__mermap_tmpa_tl }}
    \pgfpathcurveto
      { \pgfpoint { - \l__mermap_tmpb_tl } { \l__mermap_tmpa_tl }}
      { \pgfpoint { - \l__mermap_tmpa_tl } { \l__mermap_tmpb_tl }}
      { \pgfpoint { - \l__mermap_tmpa_tl } { 0pt }}
    \pgfpathcurveto
      { \pgfpoint { - \l__mermap_tmpa_tl } { - \l__mermap_tmpb_tl }}
      { \pgfpoint { - \l__mermap_tmpb_tl } { - \l__mermap_tmpa_tl }}
      { \pgfpoint { 0pt } { \fp_to_dim:n { -2* \l__mermap_marker_radius_fp - \l__mermap_marker_shift_fp }}}
    \pgfpathclose
    #1
    \__mermap_tikz_path_end:
  }

\cs_new_protected_nopar:Npn \__mermap_tikz_circle_node:
  {
    \node[circle,inner~sep=0pt,font=\mrcmarkerfont,text=mrcmarkertext,mrcnodestyle]
      {
        \hbox_set:Nn \l__mermap_tmpa_box { \mrcmarkercontents }
        \fp_compare:nNnT { \box_wd:N \l__mermap_tmpa_box } > { 1.5* \l__mermap_marker_radius_fp }
        {
          \box_resize_to_wd_and_ht:Nnn \l__mermap_tmpa_box
            { \fp_to_dim:n { 1.5* \l__mermap_marker_radius_fp } }
            { \box_ht:N \l__mermap_tmpa_box }
        }
        \box_use_drop:N \l__mermap_tmpa_box
      } ;
  }

\__mermap_new_marker_type:nn { drop }
  {
    \begin{ scope } [yshift = \fp_to_dim:n { 2* \l__mermap_marker_radius_fp + \l__mermap_marker_shift_fp } ]
    \__mermap_tikz_drop_path:n {}
    \__mermap_tikz_circle_node:
    \end{ scope }
  }

\__mermap_new_marker_type:nn { pictodrop }
  {
    \begin{ scope } [yshift = \fp_to_dim:n { 2* \l__mermap_marker_radius_fp + \l__mermap_marker_shift_fp } ]
    \__mermap_tikz_drop_path:n {}
    \tl_use:N \l__mermap_marker_pictocontents_tl
    \end{ scope }
  }

\__mermap_new_marker_type:nn { pictodropring }
  {
    \begin{ scope } [yshift = \fp_to_dim:n { 2* \l__mermap_marker_radius_fp + \l__mermap_marker_shift_fp } ]
    \group_begin:
    \pgfseteorule
    \__mermap_tikz_drop_path:n {
        \pgfpathcircle{ \pgfpoint { 0pt } { 0pt } } { \fp_to_dim:N \l__mermap_marker_inner_radius_fp }
      }
    \group_end:
    \tl_use:N \l__mermap_marker_pictocontents_tl
    \end{ scope }
  }

\__mermap_new_marker_type:nn { knob }
  {
    \path[draw=mrcmarkerdraw,fill=mrcmarkerfill,mrcpathstyle,mrchyperpath]
      circle ( \fp_to_dim:N \l__mermap_marker_radius_fp );
    \__mermap_tikz_circle_node:
  }

\__mermap_new_marker_type:nn { pictoknob }
  {
    \path[draw=mrcmarkerdraw,fill=mrcmarkerfill,mrcpathstyle,mrchyperpath]
      circle ( \fp_to_dim:N \l__mermap_marker_radius_fp );
    \tl_use:N \l__mermap_marker_pictocontents_tl
  }

\__mermap_new_marker_type:nn { pictoknobring }
  {
    \path[draw=mrcmarkerdraw,fill=mrcmarkerfill,mrcpathstyle,mrchyperpath,even~odd~rule]
      circle ( \fp_to_dim:N \l__mermap_marker_radius_fp )
      circle ( \fp_to_dim:N \l__mermap_marker_inner_radius_fp );
    \tl_use:N \l__mermap_marker_pictocontents_tl
  }

\__mermap_new_marker_type:nn { ringx }
  {
    \tl_set:Nn \l__mermap_tmpa_tl { \fp_to_dim:N \l__mermap_marker_radius_fp }
    \path[draw=mrcmarkerdraw,very~thin]
       (45  \c_colon_str \l__mermap_tmpa_tl )--(225 \c_colon_str \l__mermap_tmpa_tl )
       (135 \c_colon_str \l__mermap_tmpa_tl )--(315 \c_colon_str \l__mermap_tmpa_tl );
    \path[draw=mrcmarkerdraw,fill=mrcmarkerfill,mrcpathstyle,mrchyperpath,even~odd~rule]
      circle ( \l__mermap_tmpa_tl ) circle ( \fp_to_dim:N \l__mermap_marker_inner_radius_fp );
  }

\__mermap_new_marker_type:nn { markx }
  {
    \tl_set:Nn \l__mermap_tmpa_tl { \fp_to_dim:N \l__mermap_marker_radius_fp }
    \path[line~join=bevel,draw=mrcmarkerdraw,fill=mrcmarkerfill,mrcpathstyle,mrchyperpath]
       (0,0) -- (35 \c_colon_str \l__mermap_tmpa_tl )  -- (55 \c_colon_str \l__mermap_tmpa_tl ) -- cycle
       (0,0) -- (125 \c_colon_str \l__mermap_tmpa_tl ) -- (145 \c_colon_str \l__mermap_tmpa_tl ) -- cycle
       (0,0) -- (215 \c_colon_str \l__mermap_tmpa_tl ) -- (235 \c_colon_str \l__mermap_tmpa_tl ) -- cycle
       (0,0) -- (305 \c_colon_str \l__mermap_tmpa_tl ) -- (325 \c_colon_str \l__mermap_tmpa_tl ) -- cycle;
  }


%---- paths and routes ---------------------------------------------------------


\bool_new:N \l__mermap_path_first_point_bool


\keys_define:nn { mermap }
  {
    every~route .code:n = { \tikzset{ mermap_every_route/.style = {#1} } }
  }

\keys_set:nn { mermap }
  {
    every~route =,
  }


\cs_new:Npn \__mermap_tikz_path_begin:n #1
  {
    \path[#1] \pgfextra
  }

\cs_new:Npn \__mermap_tikz_path_end:
  {
    \endpgfextra;
  }

\cs_new_protected_nopar:Npn \__mermap_pgf_moveto_point:nn #1#2
  {
    \pgfpathmoveto { \__mermap_pgfpoint:nn {#1} {#2} }
  }

\cs_new_protected_nopar:Npn \__mermap_pgf_lineto_point:nn #1#2
  {
    \pgfpathlineto { \__mermap_pgfpoint:nn {#1} {#2} }
  }

\cs_new_protected_nopar:Npn \__mermap_route_point:nn #1#2
  {
    \bool_if:NTF \l__mermap_path_first_point_bool
    {
      \__mermap_pgf_moveto_point:nn {#1} {#2}
      \bool_set_false:N \l__mermap_path_first_point_bool
    }
    {
      \__mermap_pgf_lineto_point:nn {#1} {#2}
    }
  }

\cs_new_protected_nopar:Npn \__mermap_route_begin:n #1
  {
    \__mermap_tikz_path_begin:n
      {
        draw,mermap_every_route,#1
      }
    \cs_set_eq:NN \mrcpoint \__mermap_route_point:nn
    \bool_set_true:N \l__mermap_path_first_point_bool
  }

\NewDocumentEnvironment { mrcroute } { O {} }
  {
    \__mermap_route_begin:n {#1}
  }
  {
    \__mermap_tikz_path_end:
  }

\NewDocumentEnvironment { mrcroute* } { O {} }
  {
    \__mermap_route_begin:n {#1}
  }
  {
    \pgfpathclose
    \__mermap_tikz_path_end:
  }

\NewDocumentCommand \mrcrouteinput { s O {} m }
  {
    \__mermap_route_begin:n {#2}
    \file_input:n {#3}
    \IfBooleanT {#1}
    {
      \pgfpathclose
    }
    \__mermap_tikz_path_end:
  }


%---- orthodromes and loxodromes -----------------------------------------------


\fp_new:N \l__mermap_a_x_fp
\fp_new:N \l__mermap_a_y_fp
\fp_new:N \l__mermap_a_z_fp
\fp_new:N \l__mermap_b_x_fp
\fp_new:N \l__mermap_b_y_fp
\fp_new:N \l__mermap_b_z_fp
\fp_new:N \l__mermap_alpha_fp
\fp_new:N \l__mermap_delta_lambda_fp
\fp_new:N \l__mermap_delta_phi_fp
\fp_new:N \l__mermap_n_x_fp
\fp_new:N \l__mermap_n_y_fp
\fp_new:N \l__mermap_n_z_fp
\fp_new:N \l__mermap_psi_fp
\fp_new:N \l__mermap_sin_psi_fp
\fp_new:N \l__mermap_t_fp
\fp_new:N \l__mermap_t_psi_fp
\fp_new:N \l__mermap_t_x_fp
\fp_new:N \l__mermap_t_y_fp
\fp_new:N \l__mermap_t_z_fp
\fp_const:Nn \c__mermap_linearization_threshold_fp { 10e-9 }
\bool_new:N \l__mermap_linearization_bool

\int_new:N \l__mermap_samples_int

\keys_define:nn { mermap }
  {
    samples .int_set:N = \l__mermap_samples_int,
  }

\keys_set:nn { mermap }
  {
    samples = 100,
  }

\tikzset{
  mermap~samples/.code = { \mermapset{ samples=#1 } }
}


\cs_new_protected_nopar:Npn \__mermap_sphere_point:nnn #1#2#3  %  {x}{y}{z}
  {
    \fp_set:Nn \l__mermap_cs_lat_fp { asind(#3/sqrt((#1)^2+(#2)^2+(#3)^2)) }
    \fp_set:Nn \l__mermap_cs_lon_fp { sign(#2)*acosd(#1/sqrt((#1)^2+(#2)^2)) }
    \__mermap_pgfpoint:nn { \l__mermap_cs_lat_fp } { \l__mermap_cs_lon_fp }
  }


\cs_new_protected_nopar:Npn \__mermap_unit_sphere_point:nnn #1#2#3  %  {x}{y}{z} radius 1
  {
    \fp_set:Nn \l__mermap_cs_lat_fp { asind(#3) }
    \fp_set:Nn \l__mermap_cs_lon_fp { sign(#2)*acosd(#1/sqrt((#1)^2+(#2)^2)) }
    \__mermap_pgfpoint:nn { \l__mermap_cs_lat_fp } { \l__mermap_cs_lon_fp }
  }


\cs_new_protected:Npn \__mermap_draw_orthodrome:nnnnn #1#2#3#4#5
  {
    \__mermap_tikz_path_begin:n { draw,mermap_every_route,#1 }
    \__mermap_setup_orthodrome:nnnn { #2 } { #3 } { #4 } { #5 }
    \__mermap_pgf_moveto_point:nn { #2 } { #3 }
    \int_step_inline:nn { \l__mermap_samples_int }
      {
        \__mermap_point_on_orthodrome:n { ##1 / \l__mermap_samples_int }
        \__mermap_pgf_lineto_point:nn { \l__mermap_cs_lat_fp } { \l__mermap_cs_lon_fp }
      }
    \__mermap_tikz_path_end:
  }


\cs_new_protected:Npn \__mermap_setup_orthodrome:nnnn #1#2#3#4
  {
    \fp_set:Nn \l__mermap_a_x_fp { cosd(#2)*cosd(#1) }
    \fp_set:Nn \l__mermap_a_y_fp { sind(#2)*cosd(#1) }
    \fp_set:Nn \l__mermap_a_z_fp { sind(#1) }

    \fp_set:Nn \l__mermap_b_x_fp { cosd(#4)*cosd(#3) }
    \fp_set:Nn \l__mermap_b_y_fp { sind(#4)*cosd(#3) }
    \fp_set:Nn \l__mermap_b_z_fp { sind(#3) }

    \fp_set:Nn \l__mermap_psi_fp { acosd( sind(#1)*sind(#3) + cosd(#1)*cosd(#3)*cosd(#4-(#2)) ) }
    \fp_set:Nn \l__mermap_sin_psi_fp { sind( \l__mermap_psi_fp ) }

    \bool_set:Nn \l__mermap_linearization_bool
      {
        \fp_compare_p:nNn { \l__mermap_psi_fp } < { \c__mermap_linearization_threshold_fp }
      }
  }


\cs_new_protected:Npn \__mermap_point_on_orthodrome:n #1
  {
    \fp_set:Nn \l__mermap_t_fp { #1 }

    \bool_if:NTF \l__mermap_linearization_bool
      {
        \fp_set:Nn \l__mermap_tmpa_fp { 1 - \l__mermap_t_fp }
        \fp_set_eq:NN \l__mermap_tmpb_fp \l__mermap_t_fp
      }
      {
        \fp_set:Nn \l__mermap_t_psi_fp { \l__mermap_t_fp * \l__mermap_psi_fp }
        \fp_set:Nn \l__mermap_tmpa_fp { sind( \l__mermap_psi_fp - \l__mermap_t_psi_fp ) / \l__mermap_sin_psi_fp }
        \fp_set:Nn \l__mermap_tmpb_fp { sind( \l__mermap_t_psi_fp ) / \l__mermap_sin_psi_fp }
      }

    \fp_set:Nn \l__mermap_t_x_fp { \l__mermap_tmpa_fp * \l__mermap_a_x_fp + \l__mermap_tmpb_fp * \l__mermap_b_x_fp }
    \fp_set:Nn \l__mermap_t_y_fp { \l__mermap_tmpa_fp * \l__mermap_a_y_fp + \l__mermap_tmpb_fp * \l__mermap_b_y_fp }
    \fp_set:Nn \l__mermap_t_z_fp { \l__mermap_tmpa_fp * \l__mermap_a_z_fp + \l__mermap_tmpb_fp * \l__mermap_b_z_fp }

    \fp_set:Nn \l__mermap_cs_lat_fp { asind( \l__mermap_t_z_fp ) }
    \fp_set:Nn \l__mermap_cs_lon_fp
      {
        sign( \l__mermap_t_y_fp ) * acosd( \l__mermap_t_x_fp /sqrt( \l__mermap_t_x_fp ^2 + \l__mermap_t_y_fp ^2))
      }
  }


\cs_new_protected:Npn \__mermap_angle_at_orthodrome:
  {
    \bool_if:NTF \l__mermap_linearization_bool
    {
      \fp_set:Nn \l__mermap_alpha_fp { 90 }
    }
    {
      \fp_set:Nn \l__mermap_tmpa_fp { cosd( \l__mermap_psi_fp - \l__mermap_t_psi_fp ) }
      \fp_set:Nn \l__mermap_tmpb_fp { cosd( \l__mermap_t_psi_fp ) }
      \fp_set:Nn \l__mermap_n_x_fp { \l__mermap_tmpb_fp * \l__mermap_b_x_fp - \l__mermap_tmpa_fp * \l__mermap_a_x_fp }
      \fp_set:Nn \l__mermap_n_y_fp { \l__mermap_tmpb_fp * \l__mermap_b_y_fp - \l__mermap_tmpa_fp * \l__mermap_a_y_fp }
      \fp_set:Nn \l__mermap_n_z_fp { \l__mermap_tmpb_fp * \l__mermap_b_z_fp - \l__mermap_tmpa_fp * \l__mermap_a_z_fp }
      \fp_set:Nn \l__mermap_tmpa_fp { sqrt( \l__mermap_n_x_fp ^2 + \l__mermap_n_y_fp ^2 + \l__mermap_n_z_fp ^2 ) }
      \fp_set:Nn \l__mermap_n_x_fp { \l__mermap_n_x_fp / \l__mermap_tmpa_fp }
      \fp_set:Nn \l__mermap_n_y_fp { \l__mermap_n_y_fp / \l__mermap_tmpa_fp }
      \fp_set:Nn \l__mermap_n_z_fp { \l__mermap_n_z_fp / \l__mermap_tmpa_fp }
      \fp_set:Nn \l__mermap_alpha_fp
        {
          acosd( \l__mermap_n_y_fp *cosd( \l__mermap_cs_lon_fp ) - \l__mermap_n_x_fp * sind( \l__mermap_cs_lon_fp ) )
        }
      \fp_compare:nNnT { \l__mermap_n_z_fp } < { 0 }
        {
          \fp_set:Nn \l__mermap_alpha_fp { 360 - \l__mermap_alpha_fp }
        }
    }
  }


\NewDocumentCommand \mrcdraworthodrome { O {} mmmm }
  {
    \__mermap_draw_orthodrome:nnnnn {#1} {#2} {#3} {#4} {#5}
  }


\NewDocumentCommand \mrcNPdraworthodrome { O {} mm }
  {
    \__mermap_draw_orthodrome:nnnnn {#1} {\mrcNPlat{#2}} {\mrcNPlon{#2}} {\mrcNPlat{#3}} {\mrcNPlon{#3}}
  }


\cs_new_protected_nopar:Npn \__mermap_ortho_distance:nnnn #1#2#3#4
  {
    \fp_set:Nn \l__mermap_result_fp
    {
      \c__mermap_scale_radius_fp *
      acosd( sind(#1)*sind(#3) + cosd(#1)*cosd(#3)*cosd(#4-(#2)) )
    }
  }


\NewDocumentCommand \mrcstoreorthodistance { mmmmm }
  {
    \__mermap_ortho_distance:nnnn {#2} {#3} {#4} {#5}
    \cs_set_nopar:Npx #1 { \fp_to_decimal:N \l__mermap_result_fp }
  }


\NewDocumentCommand \mrcprettyorthodistance { mmmm }
  {
    \__mermap_ortho_distance:nnnn {#1} {#2} {#3} {#4}
    \__mermap_pretty_distance:n { \l__mermap_result_fp }
  }

\NewDocumentCommand \mrcNPprettyorthodistance { mm }
  {
    \__mermap_ortho_distance:nnnn {\mrcNPlat{#1}} {\mrcNPlon{#1}} {\mrcNPlat{#2}} {\mrcNPlon{#2}}
    \__mermap_pretty_distance:n { \l__mermap_result_fp }
  }


\cs_new_protected_nopar:Npn \__mermap_loxo_distance:nnnn #1#2#3#4
  {
    \fp_set:Nn \l__mermap_delta_phi_fp { (#3-(#1))/180*pi }
    \fp_set:Nn \l__mermap_delta_lambda_fp { (#4-(#2))/180*pi }
    \fp_compare:nNnTF { abs( \l__mermap_delta_phi_fp ) } < { 1e-5 }
      {
        \fp_set:Nn \l__mermap_tmpa_fp { tand(#1) }
        \fp_set:Nn \l__mermap_tmpb_fp
          {
            \l__mermap_delta_lambda_fp * cosd(#1)
            /( 1 + ( \l__mermap_tmpa_fp + (1+2* \l__mermap_tmpa_fp * \l__mermap_tmpa_fp )
              * \l__mermap_delta_phi_fp /3)* \l__mermap_delta_phi_fp /2 )
          }
        \fp_set:Nn \l__mermap_result_fp {
          sqrt( \l__mermap_delta_phi_fp * \l__mermap_delta_phi_fp + \l__mermap_tmpb_fp * \l__mermap_tmpb_fp ) }
      }
      {
        \fp_set:Nn \l__mermap_result_fp { abs( \l__mermap_delta_phi_fp )
          * sqrt( 1+ ( \l__mermap_delta_lambda_fp /( ln(tand(#3/2+45)) - ln(tand(#1/2+45)) ) )^2 ) }
      }
    \fp_set:Nn \l__mermap_result_fp { \c__mermap_scale_radius_fp * 180/pi * \l__mermap_result_fp }
  }


\NewDocumentCommand \mrcstoreloxodistance { mmmmm }
  {
    \__mermap_loxo_distance:nnnn {#2} {#3} {#4} {#5}
    \cs_set_nopar:Npx #1 { \fp_to_decimal:N \l__mermap_result_fp }
  }


\NewDocumentCommand \mrcprettyloxodistance { mmmm }
  {
    \__mermap_loxo_distance:nnnn {#1} {#2} {#3} {#4}
    \__mermap_pretty_distance:n { \l__mermap_result_fp }
  }


\NewDocumentCommand \mrcNPprettyloxodistance { mm }
  {
    \__mermap_loxo_distance:nnnn { \mrcNPlat{#1} } { \mrcNPlon{#1} } { \mrcNPlat{#2} } { \mrcNPlon{#2} }
    \__mermap_pretty_distance:n { \l__mermap_result_fp }
  }


\cs_new_protected:Npn \__mermap_orthodrome_point:nnn #1#2#3
  {
    \__mermap_point_on_orthodrome:n { #3 }
    \__mermap_set_named_position:nnn { #1 } { \l__mermap_cs_lat_fp } { \l__mermap_cs_lon_fp }
    \IfNoValueF { #2 }
      {
        \__mermap_angle_at_orthodrome:
        \cs_set:Npe #2 { \fp_to_decimal:N \l__mermap_alpha_fp }
      }
  }

\NewDocumentCommand \mrcNPfromOrthoFraction { mo mm mm m }
  {
    \__mermap_setup_orthodrome:nnnn { #3 } { #4 } { #5 } { #6 }
    \__mermap_orthodrome_point:nnn { #1 } { #2 } { #7 }
  }

\NewDocumentCommand \mrcNPfromOrthoFractionNamed { mo m m m }
  {
    \__mermap_setup_orthodrome:nnnn { \mrcNPlat{#3} } { \mrcNPlon{#3} } { \mrcNPlat{#4} } { \mrcNPlon{#4} }
    \__mermap_orthodrome_point:nnn { #1 } { #2 } { #5 }
  }

\NewDocumentCommand \mrcNPfromOrthoDistance { mo mm mm m }
  {
    \__mermap_ortho_distance:nnnn { #3 } { #4 } { #5 } { #6 }
    \fp_compare:nNnTF { \l__mermap_result_fp } > { 0.001 }
      {
        \__mermap_setup_orthodrome:nnnn { #3 } { #4 } { #5 } { #6 }
        \__mermap_orthodrome_point:nnn { #1 } { #2 } { #7 / \l__mermap_result_fp }
      }
      {
        \__mermap_set_named_position:nnn { #1 } { #3 } { #4 }
        \IfNoValueF { #2 }
          {
            \cs_set:Npn #2 { 90 }
          }
      }
  }

\NewDocumentCommand \mrcNPfromOrthoDistanceNamed { mo m m m }
  {
    \__mermap_ortho_distance:nnnn { \mrcNPlat{#3} } { \mrcNPlon{#3} } { \mrcNPlat{#4} } { \mrcNPlon{#4} }
    \fp_compare:nNnTF { \l__mermap_result_fp } > { 0.001 }
      {
        \__mermap_setup_orthodrome:nnnn { \mrcNPlat{#3} } { \mrcNPlon{#3} } { \mrcNPlat{#4} } { \mrcNPlon{#4} }
        \__mermap_orthodrome_point:nnn { #1 } { #2 } { #5 / \l__mermap_result_fp }
      }
      {
        \__mermap_set_named_position:nnn { #1 } { \mrcNPlat{#3} } { \mrcNPlon{#3} }
        \IfNoValueF { #2 }
          {
            \cs_set:Npn #2 { 90 }
          }
      }
  }


%---- animations ---------------------------------------------------------------

\bool_new:N \l__mermap_anim_drop_first_frame_bool
\bool_new:N \l__mermap_anim_drop_last_frame_bool

\fp_new:N \l__mermap_anim_a_lat_fp
\fp_new:N \l__mermap_anim_a_lon_fp
\fp_new:N \l__mermap_anim_a_scaledenom_fp
\fp_new:N \l__mermap_anim_a_time_fp
\fp_new:N \l__mermap_anim_b_lat_fp
\fp_new:N \l__mermap_anim_b_lon_fp
\fp_new:N \l__mermap_anim_b_scaledenom_fp
\fp_new:N \l__mermap_anim_b_time_fp
\fp_new:N \l_mermap_anim_angle_fp
\fp_new:N \l_mermap_anim_lat_fp
\fp_new:N \l_mermap_anim_lon_fp
\fp_new:N \l_mermap_anim_scaledenom_fp
\fp_new:N \l_mermap_anim_time_fp

\int_new:N \l__mermap_anim_final_frame_int
\int_new:N \l__mermap_anim_frames_int
\int_new:N \l__mermap_anim_start_frame_int
\int_new:N \l_mermap_anim_frame_int

\seq_new:N \l__mermap_anim_scaledenom_seq

\tl_new:N \l__mermap_anim_timescale_tl
\tl_new:N \l__mermap_anim_timewarp_tl


\NewExpandableDocumentCommand \mrcTimewarpIdentity { m }
  {
    #1
  }

\NewExpandableDocumentCommand \mrcTimewarpSlowStart { m m }
  {
    (#2)^(#1)
  }

\NewExpandableDocumentCommand \mrcTimewarpSlowFinal { m m }
  {
    1 - (1-#2)^(#1)
  }

\NewExpandableDocumentCommand \mrcTimewarpSlowStartFinal { m m }
  {
    %1 - (1-#2)^(#1)
    #2 <= 0.5
    ? (2*(#2))^(#1) / 2
    : 1 - (2-2*(#2))^(#1) / 2
  }


\NewDocumentEnvironment { mrcAnimation } { m +b }
  {
    \keys_set:nn { mermap/anim } { #1 }

    \__mermap_anim_autocorrect_scaledenom_seq:
    \int_compare:nNnT { \l__mermap_anim_frames_int } < { 2 }
      {
        \int_set:Nn \l__mermap_anim_frames_int { 2 }
      }
    \bool_if:NTF \l__mermap_anim_drop_first_frame_bool
      {
        \int_set:Nn \l__mermap_anim_start_frame_int { 2 }
      }
      {
        \int_set:Nn \l__mermap_anim_start_frame_int { 1 }
      }
    \bool_if:NTF \l__mermap_anim_drop_last_frame_bool
      {
        \int_set:Nn \l__mermap_anim_final_frame_int { \l__mermap_anim_frames_int - 1 }
      }
      {
        \int_set:Nn \l__mermap_anim_final_frame_int { \l__mermap_anim_frames_int }
      }

    \__mermap_setup_orthodrome:nnnn { \l__mermap_anim_a_lat_fp } { \l__mermap_anim_a_lon_fp }
                                    { \l__mermap_anim_b_lat_fp } { \l__mermap_anim_b_lon_fp }

    \__mermap_anim_pop_time_scale:NN \l__mermap_anim_a_time_fp \l__mermap_anim_a_scaledenom_fp
    \__mermap_anim_pop_time_scale:NN \l__mermap_anim_b_time_fp \l__mermap_anim_b_scaledenom_fp

    \int_step_variable:nnNn { \l__mermap_anim_start_frame_int } { \l__mermap_anim_final_frame_int }
                            \l_mermap_anim_frame_int
      {
        \fp_set:Nn \l_mermap_anim_time_fp { ( \l_mermap_anim_frame_int - 1 ) / ( \l__mermap_anim_frames_int - 1 ) }
        \fp_set:Nn \l_mermap_anim_time_fp { \l__mermap_anim_timewarp_tl { \l_mermap_anim_time_fp } }

        \__mermap_point_on_orthodrome:n { \l_mermap_anim_time_fp }
        \fp_set_eq:NN \l_mermap_anim_lat_fp \l__mermap_cs_lat_fp
        \fp_set_eq:NN \l_mermap_anim_lon_fp \l__mermap_cs_lon_fp
        \__mermap_set_named_position:nnn { AnimNP } { \l_mermap_anim_lat_fp } { \l_mermap_anim_lon_fp }

        \__mermap_angle_at_orthodrome:
        \fp_set_eq:NN \l_mermap_anim_angle_fp \l__mermap_alpha_fp

        \fp_while_do:nNnn \l_mermap_anim_time_fp > \l__mermap_anim_b_time_fp
          {
            \fp_set_eq:NN \l__mermap_anim_a_time_fp \l__mermap_anim_b_time_fp
            \fp_set_eq:NN \l__mermap_anim_a_scaledenom_fp \l__mermap_anim_b_scaledenom_fp
            \__mermap_anim_pop_time_scale:NN \l__mermap_anim_b_time_fp \l__mermap_anim_b_scaledenom_fp
          }
        \fp_set:Nn \l_mermap_anim_scaledenom_fp
          {
            round ( exp (
                ln( \l__mermap_anim_a_scaledenom_fp )
                + ( ln( \l__mermap_anim_b_scaledenom_fp )
                - ln( \l__mermap_anim_a_scaledenom_fp ) )
                * ( \l_mermap_anim_time_fp - \l__mermap_anim_a_time_fp )
                / ( \l__mermap_anim_b_time_fp - \l__mermap_anim_a_time_fp )
              ) )
          }

        #2
      }
  } {}


\cs_new_protected:Npn \__mermap_anim_autocorrect_scaledenom_seq:
  {
    \seq_get_left:NN \l__mermap_anim_scaledenom_seq \l__mermap_anim_timescale_tl
    \bool_lazy_any:nTF
      {
        { \seq_if_empty_p:N \l__mermap_anim_scaledenom_seq }
        { \tl_if_empty_p:N \l__mermap_anim_timescale_tl }
        { \quark_if_no_value_p:N \l__mermap_anim_timescale_tl }
      }
      {
        \seq_set_split:Nnn \l__mermap_anim_scaledenom_seq { - } { 0/25000 - 1/25000 },
      }
      {
        \fp_compare:nNnF { \__mermap_anim_use_time:o \l__mermap_anim_timescale_tl } = { 0 }
          {
            \seq_put_left:Ne \l__mermap_anim_scaledenom_seq
              { 0 / \__mermap_anim_use_scale:o \l__mermap_anim_timescale_tl }
          }
      }
    \seq_get_right:NNF \l__mermap_anim_scaledenom_seq \l__mermap_anim_timescale_tl
      {
        \msg_error:nn { mercatormap } { anim-scaledenom-invalid }
      }
    \fp_compare:nNnF { \__mermap_anim_use_time:o \l__mermap_anim_timescale_tl } = { 1 }
      {
        \seq_put_right:Ne \l__mermap_anim_scaledenom_seq
          { 1 / \__mermap_anim_use_scale:o \l__mermap_anim_timescale_tl }
      }
    \fp_set:Nn \l__mermap_anim_a_time_fp { -1 }
    \seq_map_inline:Nn \l__mermap_anim_scaledenom_seq
      {
        \fp_set:Nn \l__mermap_anim_b_time_fp { \__mermap_anim_use_time:n { ##1 } }
        \fp_compare:nNnTF \l__mermap_anim_a_time_fp < \l__mermap_anim_b_time_fp
          {
            \fp_set_eq:NN \l__mermap_anim_a_time_fp \l__mermap_anim_b_time_fp
          }
          {
            \msg_error:nn { mercatormap } { anim-scaledenom-invalid }
          }
      }
  }

\cs_new_protected:Npn \__mermap_anim_pop_time_scale:NN #1#2
  {
    \seq_pop_left:NN \l__mermap_anim_scaledenom_seq \l__mermap_anim_timescale_tl
    \fp_set:Nn #1 { \__mermap_anim_use_time:o \l__mermap_anim_timescale_tl }
    \fp_set:Nn #2 { \__mermap_anim_use_scale:o \l__mermap_anim_timescale_tl }
  }

\msg_new:nnnn { mercatormap } { anim-scaledenom-invalid }
  { The~animation~scale~denominator~sequence~is~invalid. }
  {
    Check~\seq_use:Nn \l__mermap_anim_scaledenom_seq { ~-~ }~with~the~documentation.~
    Time~values~are~not~strictly~monotonically~increasing.
  }

\cs_new_protected_nopar:Npn \__mermap_anim_set_position:w #1 #2 / #3 \q_stop
  {
    \fp_set:cn { l__mermap_anim_#1_lat_fp } { #2 }
    \fp_set:cn { l__mermap_anim_#1_lon_fp } { #3 }
  }

\cs_new_nopar:Npn \__mermap_anim_use_time:w #1 / #2 \q_stop
  {
    #1
  }
\cs_new_nopar:Npn \__mermap_anim_use_time:n #1
  {
    \__mermap_anim_use_time:w #1 \q_stop
  }
\cs_generate_variant:Nn \__mermap_anim_use_time:n { o }

\cs_new_nopar:Npn \__mermap_anim_use_scale:w #1 / #2 \q_stop
  {
    #2
  }
\cs_new_nopar:Npn \__mermap_anim_use_scale:n #1
  {
    \__mermap_anim_use_scale:w #1 \q_stop
  }
\cs_generate_variant:Nn \__mermap_anim_use_scale:n { o }

\keys_define:nn { mermap/anim }
  {
    start-position .code:n = \__mermap_anim_set_position:w { a } #1 \q_stop,
    final-position .code:n = \__mermap_anim_set_position:w { b } #1 \q_stop,
    position       .meta:n = { start-position = #1, final-position = #1 },
    named-start-position .code:n =
      {
        \fp_set:Nn \l__mermap_anim_a_lat_fp { \mrcNPlat{ #1 } }
        \fp_set:Nn \l__mermap_anim_a_lon_fp { \mrcNPlon{ #1 } }
      },
    named-final-position .code:n =
      {
        \fp_set:Nn \l__mermap_anim_b_lat_fp { \mrcNPlat{ #1 } }
        \fp_set:Nn \l__mermap_anim_b_lon_fp { \mrcNPlon{ #1 } }
      },
    named-position     .meta:n     = { named-start-position = #1, named-final-position = #1 },
    frames             .int_set:N  = \l__mermap_anim_frames_int,
    drop-first-frame   .bool_set:N = \l__mermap_anim_drop_first_frame_bool,
    drop-last-frame    .bool_set:N = \l__mermap_anim_drop_last_frame_bool,
    drop-no-frame      .meta:n     = { drop-first-frame=false, drop-last-frame=false },
    scale-denominators .code:n     = \seq_set_split:Nnn \l__mermap_anim_scaledenom_seq { - } { #1 },
    common-scale-denominator .meta:n = { scale-denominators = 0/#1 - 1/#1 },
    timewarp           .tl_set:N   = \l__mermap_anim_timewarp_tl,
    timewarp-identity         .meta:n = { timewarp = \mrcTimewarpIdentity },
    timewarp-slow-start       .meta:n = { timewarp = \mrcTimewarpSlowStart{#1} },
    timewarp-slow-start       .default:n = 2,
    timewarp-slow-final       .meta:n = { timewarp = \mrcTimewarpSlowFinal{#1} },
    timewarp-slow-final       .default:n = 2,
    timewarp-slow-start-final .meta:n = { timewarp = \mrcTimewarpSlowStartFinal{#1} },
    timewarp-slow-start-final .default:n = 2,
  }

\keys_set:nn { mermap/anim }
  {
    position = 51.4779 / 0,
    frames   = 20,
    drop-no-frame,
    common-scale-denominator = 25000,
    timewarp-identity,
  }

\NewExpandableDocumentCommand\mrcAnimFrame      {} { \int_to_arabic:n { \l_mermap_anim_frame_int }}
\NewExpandableDocumentCommand\mrcAnimTime       {} { \fp_to_decimal:N \l_mermap_anim_time_fp }
\NewExpandableDocumentCommand\mrcAnimScaleDenom {} { \fp_to_decimal:N \l_mermap_anim_scaledenom_fp }
\NewExpandableDocumentCommand\mrcAnimLatitude   {} { \fp_to_decimal:N \l_mermap_anim_lat_fp }
\NewExpandableDocumentCommand\mrcAnimLongitude  {} { \fp_to_decimal:N \l_mermap_anim_lon_fp }
\NewExpandableDocumentCommand\mrcAnimAngle      {} { \fp_to_decimal:N \l_mermap_anim_angle_fp }


%---- supplier -----------------------------------------------------------------

\file_input:n { \mrcpkgprefix mercatorsupplier.def }
