# How to find coordinates of intersecting circles using TikZ?

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}
\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\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):

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

Here's a possibility using the intersections library:

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}
\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\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

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

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);