3

I'm using code based on Tomas M. Trzeciak’s Stereographic and cylindrical map projections example to draw spheres.

I have added three great circles to the sphere: The equator, and two longitudinal circles. I want to mark the (frontal) intersections of the equator and the other two great circles with dots (just as I have with the zenith and nadir), and label them A and B. For this, I probably need to use the coordinates of the intersections of the circles somehow. How do I do this?

Here's a MWE.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc,fadings,decorations.pathreplacing}
\usepackage{verbatim}

%% Helper macros

\newcommand\pgfmathsinandcos[3]{%
  \pgfmathsetmacro#1{sin(#3)}%
  \pgfmathsetmacro#2{cos(#3)}%
}

\newcommand\LongitudePlane[3][current plane]{%
  \pgfmathsinandcos\sinEl\cosEl{#2} % elevation
  \pgfmathsinandcos\sint\cost{#3} % azimuth
  \tikzset{#1/.estyle={cm={\cost,\sint*\sinEl,0,\cosEl,(0,0)}}}
}

\newcommand\LatitudePlane[3][current plane]{%
  \pgfmathsinandcos\sinEl\cosEl{#2} % elevation
  \pgfmathsinandcos\sint\cost{#3} % latitude
  \pgfmathsetmacro\yshift{\cosEl*\sint}
  \tikzset{#1/.estyle={cm={\cost,0,0,\cost*\sinEl,(0,\yshift)}}} %
}

\newcommand\DrawLongitudeCircle[2][1]{
  \LongitudePlane{\angEl}{#2}
  \tikzset{current plane/.prefix style={scale=#1}}
   % angle of "visibility"
  \pgfmathsetmacro\angVis{atan(sin(#2)*cos(\angEl)/sin(\angEl))} %
  \draw[current plane] (\angVis:1) arc (\angVis:\angVis+180:1);
  \draw[current plane,dashed] (\angVis-180:1) arc (\angVis-180:\angVis:1);
}

\newcommand\DrawLatitudeCircle[2][2]{
  \LatitudePlane{\angEl}{#2}
  \tikzset{current plane/.prefix style={scale=#1}}
  \pgfmathsetmacro\sinVis{sin(#2)/cos(#2)*sin(\angEl)/cos(\angEl)}
  % angle of "visibility"
  \pgfmathsetmacro\angVis{asin(min(1,max(\sinVis,-1)))}
  \draw[current plane] (\angVis:1) arc (\angVis:-\angVis-180:1);
  \draw[current plane,dashed] (180-\angVis:1) arc (180-\angVis:\angVis:1);
}

%% Document-wide tikz options and styles

\tikzset{%
  >=latex, % option for nice arrows
  inner sep=0pt,%
  outer sep=2pt,%
  mark coordinate/.style={inner sep=0pt,outer sep=0pt,minimum size=3pt,
    fill=black,circle}%
}

\begin{document}

\begin{tikzpicture} % "THE GLOBE" showcase

\def\R{2.5} % sphere radius
\def\angEl{25} % elevation angle
\filldraw[ball color=white] (0,0) circle (\R);
\DrawLatitudeCircle[\R]{0}
\DrawLongitudeCircle[\R]{-110}
\DrawLongitudeCircle[\R]{-50}

\pgfmathsetmacro\H{\R*cos(\angEl)} % distance to north pole
\coordinate (O) at (0,0);
\coordinate[mark coordinate] (Z) at (0,\H);
\coordinate[mark coordinate] (N) at (0,-\H);

\draw[->] (0,-\H) -- (0,1.2*\R) node[above] {Zenith};
\draw[dashed] (0,-\H) -- (0,-\R);
\draw (0,-\R) -- (0,-1.2*\R) node[below] {Nadir};

\end{tikzpicture}

\end{document}

It produces the image below (sans the obvious modifications):

enter image description here

vedadh
  • 95
  • Using your mwe I don't see the equator or the two longitudes. Also, when I create a macro I always document the arguments. – John Kormylo Jul 09 '14 at 15:44
  • I just used the MWE myself, and all circles appeared as they should. I draw the equator and the two longitudes with \DrawLatitudeCircle[\R]{0}, \DrawLongitudeCircle[\R]{-110}, \DrawLongitudeCircle[\R]{-50}. The macros are defined a bit further up. I apologise for the lack of documentation on the macros, but as I mentioned in my post I use existing code, so I didn't write the macros myself. – vedadh Jul 09 '14 at 16:07
  • First problem: you can't put numbers into macro names. \angE1 converts to \angE 1 – John Kormylo Jul 09 '14 at 18:13
  • 1
    @JohnKormylo I think angEl has an ell not a one... – Thruston Jul 09 '14 at 21:00
  • @Thruston is correct. It's the letter l, not the number 1, so the MWE is working fine. – vedadh Jul 10 '14 at 08:54
  • @vedadh your code works correctly for PGF/TikZ version 2.10 but not for version 3.10; for version 3.10 the estyle have to be changed to style (see the remark to the answer I provided below). I'd suggest you to update your LaTeX system; in the meantime, for my answer to work for you with your PGF version,you will need to change back style to estyle in the two lines I mentioned in my answer. – Gonzalo Medina Jul 10 '14 at 14:48
  • In that case, I apologise to John Kormylo for my ignorance. I wasn't aware that I had an outdated version. @GonzaloMedina thank you for pointing it out to me. EDIT: I'll make sure to update my LaTeX system. – vedadh Jul 10 '14 at 15:15

2 Answers2

4

Here's a possibility using the intersections library:

enter image description here

I redefined \DrawLongitudeCircle and \DrawLatitudeCircle to have one more mandatory argument that will be used to name the paths; once the naming is done, the intersections library is used to get the intersection points between the paths.

The code:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{intersections,calc,fadings,decorations.pathreplacing}
\usepackage{verbatim}
\usepackage{xparse}

%% Helper macros

\newcommand\pgfmathsinandcos[3]{%
  \pgfmathsetmacro#1{sin(#3)}%
  \pgfmathsetmacro#2{cos(#3)}%
}

\newcommand\LongitudePlane[3][current plane]{%
  \pgfmathsinandcos\sinEl\cosEl{#2} % elevation
  \pgfmathsinandcos\sint\cost{#3} % azimuth
  \tikzset{#1/.style={cm={\cost,\sint*\sinEl,0,\cosEl,(0,0)}}}
}

\newcommand\LatitudePlane[3][current plane]{%
  \pgfmathsinandcos\sinEl\cosEl{#2} % elevation
  \pgfmathsinandcos\sint\cost{#3} % latitude
  \pgfmathsetmacro\yshift{\cosEl*\sint}
  \tikzset{#1/.style={cm={\cost,0,0,\cost*\sinEl,(0,\yshift)}}} %
}

\newcommand\DrawLongitudeCircle[3][1]{
  \LongitudePlane{\angEl}{#2}
  \tikzset{current plane/.prefix style={scale=#1}}
   % angle of "visibility"
  \pgfmathsetmacro\angVis{atan(sin(#2)*cos(\angEl)/sin(\angEl))} %
  \draw[name path=#3,current plane] (\angVis:1) arc (\angVis:\angVis+180:1);
  \draw[name path=dashed#3,current plane,dashed] (\angVis-180:1) arc (\angVis-180:\angVis:1);
}

\newcommand\DrawLatitudeCircle[3][2]{
  \LatitudePlane{\angEl}{#2}
  \tikzset{current plane/.prefix style={scale=#1}}
  \pgfmathsetmacro\sinVis{sin(#2)/cos(#2)*sin(\angEl)/cos(\angEl)}
  % angle of "visibility"
  \pgfmathsetmacro\angVis{asin(min(1,max(\sinVis,-1)))}
  \draw[name path=#3,current plane] (\angVis:1) arc (\angVis:-\angVis-180:1);
  \draw[name path=dashed#3,current plane,dashed] (180-\angVis:1) arc (180-\angVis:\angVis:1);
}

%% Document-wide tikz options and styles

\tikzset{%
  >=latex, % option for nice arrows
  inner sep=0pt,%
  outer sep=2pt,%
  mark coordinate/.style={inner sep=0pt,outer sep=0pt,minimum size=3pt,
    fill=black,circle},%
smalldot/.style={
  fill,
  circle,
  inner sep=1.5pt
  }
}

\begin{document}

\begin{tikzpicture} % "THE GLOBE" showcase

\def\R{2.5} % sphere radius
\def\angEl{25} % elevation angle
\filldraw[ball color=white] (0,0) circle (\R);
\DrawLatitudeCircle[\R]{0}{lat}
\DrawLongitudeCircle[\R]{-110}{lon1}
\DrawLongitudeCircle[\R]{-50}{lon2}

% We find the intersection points and then draw little filled circles at them    
\path[name intersections={of= lat and lon1,by={a}}];
\path[name intersections={of= lat and lon2,by={b}}];
\node[smalldot,label={above right:$A$}] at (a) {};
\node[smalldot,label={above left:$B$}] at (b) {};

\pgfmathsetmacro\H{\R*cos(\angEl)} % distance to north pole
\coordinate (O) at (0,0);
\coordinate[mark coordinate] (Z) at (0,\H);
\coordinate[mark coordinate] (N) at (0,-\H);

\draw[->] (0,-\H) -- (0,1.2*\R) node[above] {Zenith};
\draw[dashed] (0,-\H) -- (0,-\R);
\draw (0,-\R) -- (0,-1.2*\R) node[below] {Nadir};

\end{tikzpicture}

\end{document}

Remark

For PGF/TikZ version 3.0 the code given in the original question has to be modified to produce the desired result by changing (as in my example code) estyle to style in the lines

  \tikzset{#1/.estyle={cm={\cost,\sint*\sinEl,0,\cosEl,(0,0)}}}

and

  \tikzset{#1/.estyle={cm={\cost,0,0,\cost*\sinEl,(0,\yshift)}}} %

In any case, my solution will work for any version > 2.10.

Gonzalo Medina
  • 505,128
  • Thanks for the great solution! I was onto the idea of adding an argument to the latitudinal and longitudinal macros that defines the path names, but I wasn't too confident on the syntax, so I didn't get very far. – vedadh Jul 10 '14 at 15:16
2

For what it's worth, here is a version done in Metapost.

enter image description here

Finding the intersections is easy using the intersectionpoint construct. But as you'll see I've cheated a bit by keeping the north and south poles at the top and bottom. To rotate the figure freely as in your example would require working out all the projections, which would be a bit tedious in plain MP (but not impossible).

prologues := 3;
outputtemplate := "%j%c.eps";

beginfig(1);

path outline, equator, prime_meridian, ninety_meridian;

r := 100;
outline := fullcircle scaled 2r;

n := 40;
for i = 0 upto n:
  fill outline scaled (1-3i/4n) shifted (-i*r/2.8n,i*r/2.8n) withcolor (0.5+i/1.6n)*white;
endfor

equator         = outline yscaled 0.14;
prime_meridian  = outline xscaled 0.54;
ninety_meridian = outline xscaled 0.84;

drawoptions(dashed withdots withcolor .3 white);
draw subpath(0,4) of equator; 
draw subpath(-2,2) of prime_meridian;
draw subpath(2,6) of ninety_meridian;

drawoptions(dashed evenly withcolor .3 white);
draw r*down -- r*up;

drawoptions();
draw subpath(4,8) of equator;
draw subpath(2,6) of prime_meridian;
draw subpath(-2,2) of ninety_meridian;

dotlabel.urt ("A",subpath  (3,5) of prime_meridian  intersectionpoint subpath (4,6) of equator);
dotlabel.ulft("B",subpath (-1,1) of ninety_meridian intersectionpoint subpath (6,8) of equator);

drawarrow r*up   -- 1.2r*up;
draw      r*down -- 1.2r*down;

label.top("Zenith",1.2r*up);
label.bot("Nadir", 1.2r*down);

endfig;
end.
enter code here
Thruston
  • 42,268
  • Thanks for the suggestion, but I'd rather keep to TikZ, and to have the poles at the right positions depending on the orientation of the sphere. – vedadh Jul 10 '14 at 15:26
  • @vedadh no problem, I only post MP solutions to keep the TikZ boys on their toes! – Thruston Jul 10 '14 at 17:08