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

  • 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 9 '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 9 '14 at 16:07
  • First problem: you can't put numbers into macro names. \angE1 converts to \angE 1 – John Kormylo Jul 9 '14 at 18:13
  • 1
    @JohnKormylo I think angEl has an ell not a one... – Thruston Jul 9 '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 8:54
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.

  • 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
  • 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

Your Answer

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

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