7

I'm trying to make a graph that looks like this:

A map projection with labelled gridlines

This code gets close:

GeoGraphics[GeoRange -> {{20, 35}, {110, 140}}, 
 GeoRangePadding -> None, 
 GeoBackground -> 
  GeoStyling[{"Coastlines", "Land" -> RGBColor[0.65, 0.65, 0.65], 
    "Ocean" -> White}], GeoGridLines -> {3, 7}]

enter image description here

But I can't figure out how to get the gridlines to be labeled with $110^\circ \text{E}$ etc. I guess it can be hard-coded with some annoying combination of Legended and Placed but there's got to be a better way? None of the examples on the GeoGraphics reference look quite like this.

Mr. G
  • 335
  • 1
  • 8

1 Answers1

3

Edit May 3, 2023 (most recent)

The latest version of this function is being developed (for some definition of the word "developed") here. The main function is FancyGeoFrame[ticksX, ticksY, opts].

SetDirectory[NotebookDirectory[]];
<<FancyGeoFrame`

(* The frame will have four black corners can therefore look slightly nicer if there are an even number of X and Y ticks, but it isn't required. *) ticksX={110,115,120,125,130,135,140}; ticksY={20,23,26,29,32,35};

{frame,newrange} = FancyGeoFrame[ticksX, ticksY, FancyBoxesSize->{{0.5,0.5},{0.45,0.45}}, FancyTicksPadding->{2,3}, FancyTicksMag->2.5, FancyTicksAngle->{0,-5}*Pi/180, FancyTicksYDelta->0.2];

world={ GeoStyling[Opacity[1]], FaceForm[LightGray], EdgeForm[None], CountryData["World", "Polygon"] };

GeoGraphics[ Join[world,frame], GeoRange->Reverse[newrange], GeoRangePadding->{None,None}, GeoBackground->None, GeoGridLines->None, ImageSize->1100 ]

enter image description here

The following options are available

  • FancyBoxesSize -> {{left, right}, {bottom, top}} specifies the width of the boxes on each side.
  • FancyTicksPadding->{bottom, left} specifies how far away the tick labels are from the frame.
  • FancyTicksMag->mag specifies how large the tick labels are.
  • FancyGridlinesOpacity->op specifies the opacity of the gridlines.
  • FancyTicksAngle->{bottom, left} specifies the rotation of the labels. On the y axis, the labels are rotated uniformly and on the x axis they are rotated concentrically. This can make the labels look nicer if their angle roughly matches the angle of the gridlines.
  • FancyTicksYDelta->delta specifies the "slope" of the labels on the y axis. This can make the labels look nicer if the lower labels are too far away from the y axis.

Original Answer

I have written the following function to create the frame and axes labels. This function does not "just work" by any stretch of the imagination, it requires a lot of parameter tweaking to get it to look right and I'm truly sorry if you have to use this. Note that I'm using MaTeX to get a professional font.

ConstructFrame[georange_, nx_, ny_, deltax_, deltay_, paddingsizex_, 
  paddingsizey_, axesmag_, textmag_, angle_, slope_] := 
 Module[{xrng, yrng, bw, labelerx, labelery, boxes = {}, 
   paddingboxes = {}, gridlines = {}, xlabels = {}, ylabels = {}, 
   world},
  xrng = 
   Range[georange[[2, 1]], georange[[2, 2]], 
    Ceiling[(georange[[2, 2]] - georange[[2, 1]])/nx]];
  yrng = 
   Range[georange[[1, 1]], georange[[1, 2]], 
    Ceiling[(georange[[1, 2]] - georange[[1, 1]])/ny]];
  If[xrng[[-1]] != georange[[2, 2]], AppendTo[xrng, georange[[2, 2]]]];
  If[yrng[[-1]] != georange[[1, 2]], AppendTo[yrng, georange[[1, 2]]]];
  bw[i_] := If[OddQ[i], Black, White];
  (*Constructing x boxes*);
  Do[
   AppendTo[boxes,
    {GeoStyling[Opacity[1]], FaceForm[bw[i]], EdgeForm[Black], 
     GeoBoundsRegion[{{georange[[1, 1]], 
        georange[[1, 1]] + deltax}, {xrng[[i]], xrng[[i + 1]]}}]}
    ];
   AppendTo[boxes,
    {GeoStyling[Opacity[1]], FaceForm[bw[i]], EdgeForm[Black], 
     GeoBoundsRegion[{{georange[[1, 2]], 
        georange[[1, 2]] - deltax}, {xrng[[i]], xrng[[i + 1]]}}]}
    ],
   {i, 1, Length[xrng] - 1}
   ];
  (*Constructing y boxes*);
  Do[
   AppendTo[boxes,
    {GeoStyling[Opacity[1]], FaceForm[bw[i]], EdgeForm[Black], 
     GeoBoundsRegion[{{yrng[[i]], yrng[[i + 1]]}, {georange[[2, 1]], 
        georange[[2, 1]] + deltay}}]}
    ];
   AppendTo[boxes,
    {GeoStyling[Opacity[1]], FaceForm[bw[i]], EdgeForm[Black], 
     GeoBoundsRegion[{{yrng[[i]], 
        yrng[[i + 1]]}, {georange[[2, 2]] - deltay, 
        georange[[2, 2]]}}]}
    ],
   {i, 1, Length[yrng] - 1}
   ];
  (*Constructing x padding boxes*);
  AppendTo[paddingboxes,
   {GeoStyling[Opacity[1]], FaceForm[White], EdgeForm[White], 
    GeoBoundsRegion[{{georange[[1, 1]] - 0.07, 
       georange[[1, 1]] - paddingsizex - 1}, {-179.9, 179.9}}]}
   ];
  (*Constructing y padding boxes*);
  AppendTo[paddingboxes,
   {GeoStyling[Opacity[1]], FaceForm[White], EdgeForm[White], 
    GeoBoundsRegion[{{-89.9, 89.9}, {georange[[2, 1]] - paddingsizey, 
       georange[[2, 1]] - 0.03}}]}
   ];
  (*Constructing x gridlines*);
  Do[AppendTo[gridlines,
    {Opacity[0.2], Black, 
     GeoPath[{{georange[[1, 1]], xrng[[i]]}, {georange[[1, 2]], 
        xrng[[i]]}}]}], {i, 1, Length[xrng]}];
  (*Constructing y gridlines*);
  Do[AppendTo[gridlines,
    {Opacity[0.2], Black, 
     GeoPath[{{yrng[[i]], georange[[2, 1]]}, {yrng[[i]], 
        georange[[2, 2]]}}, "Rhumb"]}], {i, 1, Length[yrng]}];
  (*Constructing x labels*);
  labelerx[xval_] := 
   If[xval == 0, MaTeX["0^\\circ", Magnification -> textmag], 
    If[xval < 0, 
     MaTeX[ToString[Abs[xval]] <> "^\\circ \\, \\text{W}", 
      Magnification -> textmag], 
     MaTeX[ToString[xval] <> "^\\circ \\, \\text{E}", 
      Magnification -> textmag]]];
  Do[AppendTo[xlabels,
    {GeoMarker[{georange[[1, 1]] - paddingsizex/4, xrng[[i]]}, 
      Text[labelerx[xrng[[i]]]], "Alignment" -> Center, 
      "Scale" -> axesmag]}], {i, 2, Length[xrng] - 1}];
  (*Constructing y labels*);
  labelery[yval_] := 
   If[yval == 0, MaTeX["0^\\circ", Magnification -> textmag], 
    If[yval < 0, 
     MaTeX[ToString[Abs[yval]] <> "^\\circ \\, \\text{S}", 
      Magnification -> textmag], 
     MaTeX[ToString[yval] <> "^\\circ \\, \\text{N}", 
      Magnification -> textmag]]];
  Do[AppendTo[ylabels,
    {GeoMarker[{yrng[[i]], 
       georange[[2, 1]] - paddingsizey/1.1 - 
        slope*(i - Length[yrng/2])}, 
      Rotate[ Text[labelery[yrng[[i]]]], angle], 
      "Alignment" -> Center, "Scale" -> axesmag]}], {i, 1, 
    Length[yrng] - 1}];
  (*world shapes*);
  world = {GeoStyling[Opacity[0.5]], 
    FaceForm[RGBColor[0.65, 0.65, 0.65]], 
    EntityValue[Entity["GeographicRegion", "World"], 
     EntityProperty["GeographicRegion", "Polygon"]]};
  Join[{world, boxes, gridlines, paddingboxes, xlabels, ylabels}]
  ]

Now we can make the plot.

RANGE = {{20, 35}, {110, 140}};
XLines = 8;
YLines = 5;
FrameX = 0.2;
FrameY = 0.3;
PaddingX = 2.5;
PaddingY = 1.5;
MarkerScale = 1;
AxesLabelScale = 2;
Angle = (\[Pi]/180)*(-10);
Slope = 0.07;
frame = ConstructFrame[RANGE, XLines, YLines, FrameX, FrameY, 
   PaddingX, PaddingY, MarkerScale, AxesLabelScale, Angle, Slope];
RANGE = RANGE - {{PaddingX, 0}, {PaddingY, 0}};

GeoGraphics[frame, GeoRange -> RANGE, GeoRangePadding -> None, GeoBackground -> White, GeoGridLines -> None, ImageSize -> 1100]

enter image description here

Mr. G
  • 335
  • 1
  • 8