Skip to content

Instantly share code, notes, and snippets.

@Fil
Last active February 1, 2018 16:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Fil/1c2f954201523af16280db018ddd90cc to your computer and use it in GitHub Desktop.
Save Fil/1c2f954201523af16280db018ddd90cc to your computer and use it in GitHub Desktop.
Missions to Mars Urquhart Graph
license: mit
!function(r,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("d3-array"),require("d3-collection"),require("d3-geo"),require("d3-voronoi")):"function"==typeof define&&define.amd?define(["exports","d3-array","d3-collection","d3-geo","d3-voronoi"],e):e(r.d3=r.d3||{},r.d3,r.d3,r.d3,r.d3)}(this,function(r,e,n,o,t){"use strict";function s(r,e){for(var n=0,o=0;o<3;o++)n+=r[o]*e[o];return n}function i(r,e){for(var n=new Array(3),o=0;o<3;o++){var t=o+1;t>=3&&(t-=3);var s=o+2;s>=3&&(s-=3),n[o]=r[t]*e[s]-r[s]*e[t]}return n}function a(r,e,n){return s(i(r,e),n)}function v(r,e){for(var n=0,o=0,t=0;t<3;t++){var s=e[t]-r[t];n+=s*s;var i=e[t]+r[t];o+=i*i}return Math.sqrt(n)-Math.sqrt(o)}function u(r){for(var e,n=new Array(3),o=0,t=0;t<3;t++){var s=r[t];o+=s*s}e=o>0?1/Math.sqrt(o):0;for(var t=0;t<3;t++)n[t]=e*r[t];return n}function f(r,e,n){return i(r[e],r[n])}function l(r,e,n,o){return a(r[e],r[n],r[o])}function p(r,e,n){return v(r[e],r[n])}function d(){for(var r=new Array(3),e=0;e<3;e++)r[e]=0;return r}function g(r){for(var e=new Array(3),n=0;n<3;n++)e[n]=r[n];return e}function h(r,e){for(var n=0;n<3;n++)r[n]+=e[n]}function c(r,e){for(var n=0;n<3;n++)r[n]*=e}function y(r,e){for(var n=d(),o=0;o<3;o++)n[o]=r[o]-e[o];return n}function _(r){return"undefined"==typeof r}function b(r,e){if(_(r))return!1;if(_(e))return!1;for(var n=0;n<3;n++)if(r.verts[n]!=e.verts[n])return!1;return!0}function I(r,e){if(_(r))return!1;if(_(e))return!1;for(var n=0;n<2;n++)if(r.verts[n]!=e.verts[n])return!1;return!0}function x(r,e){return e>r?e:r}function w(r,e){this.verts=e,this.edges=new Array(3),this.dirs=new Array(3);for(var n=0;n<3;n++){var o=n+1;o>=3&&(o-=3);var t=n+2;t>=3&&(t-=3),this.dirs[n]=f(r,e[o],e[t])}this.vol=l(r,e[0],e[1],e[2]);for(var n=0;n<3;n++)c(this.dirs[n],1/this.vol);for(var s=d(),n=0;n<3;n++)h(s,this.dirs[n]);this.ccdir=u(s);for(var i=0,n=0;n<3;n++)i+=v(this.ccdir,r[e[n]]);i/=3,this.ccdsq=i}function k(r){this.verts=r,this.polys=new Array(2)}function m(r,e){this.verts=e,this.pdst=p(r,e[0],e[1]),this.direc=u(f(r,e[0],e[1]));var n=d();h(n,r[e[0]]),h(n,r[e[1]]),this.midpnt=u(n)}function V(r,e){for(var n=0;n<r.length;n++)if(e==r[n])return;r.push(e)}function A(r,e){for(var n=0;n<r.length;n++)if(I(r[n],e))return;r.push(e)}function P(r,e){for(var n=[],o=0;o<r.length;o++)for(var t=r[o],s=0;s<e.length;s++){var i=e[s];if(t==i){n.push(t);break}}return n}function q(r,e){for(var n=[],o=0;o<r.length;o++)for(var t=r[o],s=0;s<e.length;s++){var i=e[s];if(I(t,i)){n.push(t);break}}return n}function C(r,e){if(0!=e.length){for(var n=[],o=0;o<r.length;o++){for(var t=r[o],s=!0,i=0;i<e.length;i++){var a=e[i];if(a==t){s=!1;break}}s&&n.push(t)}r.splice(0,r.length);for(var v=0;v<n.length;v++)r.push(n[v])}}function O(r,e){if(0!=e.length){for(var n=[],o=0;o<r.length;o++){for(var t=r[o],s=!0,i=0;i<e.length;i++){var a=e[i];if(I(t,a)){s=!1;break}}s&&n.push(t)}r.splice(0,r.length);for(var v=0;v<n.length;v++)r.push(n[v])}}function M(r,e){for(var n=r.positions,o=n[e],t=r.triangles.length,s=0;s<t;s++){var i=r.triangles[s];if(i.IsPointInside(o)){for(var a=i.edges,v=[],u=0;u<3;u++)v.push(a[u].PolyIndexIn(i));for(var f=Array(3),l=Array(3),u=0;u<3;u++){var p=u+1;p>=3&&(p-=3),f[u]=new w(n,[i.verts[u],i.verts[p],e]),l[u]=new k([i.verts[u],e])}for(var u=0;u<3;u++){var p=u+1;p>=3&&(p-=3),f[u].edges[0]=l[p],f[u].edges[1]=l[u],l[u].polys[0]=f[u],l[p].polys[1]=f[u]}for(var u=0;u<3;u++)for(var d=a[u],g=v[u],h=0;h<3;h++)for(var c=f[h],y=0,_=0;_<2;_++)if(c.IsVertex(d.verts[_])&&y++,2==y){d.polys[g]=c,c.edges[2]=d;break}r.triangles[s]=f[0];for(var u=1;u<3;u++)r.triangles.push(f[u]);for(var u=0;u<3;u++)r.edges.push(l[u]);return!0}}return!1}function j(r){for(var e=r.positions,n=new Array(4),o=0;o<100;o++){for(var t=0,s=0;s<r.edges.length;s++){var i=r.edges[s],a=i.polys;if(!_(a[0])&&!_(a[1])){for(var v=0;v<3;v++){var u=a[0].verts[v];if(!i.IsVertex(u))break}var f=v+1;f>=3&&(f-=3);var l=v+2;l>=3&&(l-=3),n[0]=u,n[1]=a[0].verts[f],n[3]=a[0].verts[l];for(var v=0;v<3;v++){var u=a[1].verts[v];if(!i.IsVertex(u))break}n[2]=u;var p=a[0].IsPointInCircumcircle(e[n[2]]),d=a[1].IsPointInCircumcircle(e[n[0]]);if(p||d){var g=new w(e,[n[0],n[1],n[2]]);if(g.IsVertexOrderCorrect()){var h=new w(e,[n[0],n[2],n[3]]);if(h.IsVertexOrderCorrect()){t++;for(var v=0;v<3;v++){var c=a[0].edges[v];if(!I(c,i)&&c.IsVertex(n[3])){var y=c,b=v;break}}for(var v=0;v<3;v++){var c=a[1].edges[v];if(!I(c,i)&&c.IsVertex(n[1])){var x=c,k=v;break}}var m=y.PolyIndexIn(a[0]),V=x.PolyIndexIn(a[1]);y.polys[m]=a[1],x.polys[V]=a[0],a[0].edges[b]=x,a[1].edges[k]=y,a[0].copy_vert_info(g),a[1].copy_vert_info(h),i.verts=[n[0],n[2]]}}}}}if(0==t)break}}function L(r){for(var e=new Object,n=-1,o=0;o<r.edges.length;o++){var t=r.edges[o];if(_(t.polys[0])){if(_(t.polys[1]))continue;var s=t.polys[1]}else{if(!_(t.polys[1]))continue;var s=t.polys[0]}var i=t.verts[0],a=t.verts[1],v=s.VertexIndexIn(a)-s.VertexIndexIn(i);if(v<0&&(v+=3),1!=v){var u=i;i=a,a=u}e[i]=a,n=i}if(n>=0){for(var f=n,l=[f];;){var p=e[f];if(p==n)break;l.push(p),f=p}r.hull=l}}function S(r){if(1!=r.triangles.length){if(0!=r.triangles.length){for(var e=0;e<r.triangles.length;e++){var n=r.triangles[e];n.index=e,r.vor_positions.push(n.ccdir)}for(var e=0;e<r.edges.length;e++){var o=r.edges[e];if(!_(o.polys[0])&&!_(o.polys[1])){var t=[o.polys[0].index,o.polys[1].index];r.vor_edges.push(t)}}for(var e=0;e<r.indices.length;e++){var s=r.indices[e];r.vor_polygons[s]=new Object;var a=r.vor_polygons[s];a.edges=[],a.triangles=[],a.boundary=[]}for(var e=0;e<r.edges.length;e++)for(var o=r.edges[e],f=0;f<2;f++)r.vor_polygons[o.verts[f]].edges.push(o);for(var e=0;e<r.triangles.length;e++)for(var n=r.triangles[e],f=0;f<3;f++)r.vor_polygons[n.verts[f]].triangles.push(n);for(var e=0;e<r.indices.length;e++){var s=r.indices[e],a=r.vor_polygons[s],l=a.triangles[0],n=l;a.boundary.push(n.index);for(var f=0;f<3;f++){var o=n.edges[f];if(o.IsVertex(s))break}for(var p=o,x=!1;;){var k=o.PolyIndexIn(n);if(n=o.polys[1-k],_(n))break;if(b(n,l)){x=!0;break}a.boundary.push(n.index);for(var f=0;f<3;f++){var m=n.edges[f];if(!I(m,o)&&m.IsVertex(s)){o=m;break}}}if(!x){a.boundary.reverse(),n=l;for(var f=0;f<3&&(o=n.edges[f],I(o,p)||!o.IsVertex(s));f++);for(;;){var k=o.PolyIndexIn(n);if(n=o.polys[1-k],_(n))break;a.boundary.push(n.index);for(var f=0;f<3;f++){var m=n.edges[f];if(!I(m,o)&&m.IsVertex(s)){o=m;break}}}}x||(a.boundary.reverse(),a.boundary.push(-1),a.boundary.reverse(),a.boundary.push(-1))}if(r.hull.length>=3){for(var V=new Array,A=r.positions,C=r.hull.length,f=0;f<C;f++){var s=r.hull[f],O=f+1;O>=C&&(O=0);var M=r.hull[O],j=r.vor_polygons[s].edges,L=r.vor_polygons[M].edges,S=q(j,L),o=S[0],F=o.polys[0].index,E=A[s],z=A[M],D=y(z,E),T=[F,r.vor_positions[F],s,E,M,z,D];V.push(T)}for(;V.length>3;){for(var B=V.length,G=new Array,H=new Array,J=0;J<B;J++)H.push(new Array);for(var J=0;J<B;J++){var K=J+1;K>=B&&(K=0);var N=u(i(V[J][6],V[K][6]));c(N,-1),H[J].push(G.length),H[K].push(G.length),G.push(N)}for(var J=0;J<B;J++){var Q=H[J];if(Q.length>=2){for(var R=new Array,U=0;U<Q.length;U++)R.push(v(G[Q[U]],V[J][1]));for(var W=0,X=R[W],Y=0;Y<R.length;Y++){var Z=R[Y];Z<X&&(W=Y,X=Z)}var $=Q[W]}else if(1==Q.length)var $=Q[0];else var $=-1;H[J]=$}for(var rr=new Array,J=0;J<B;J++){var K=J+1;K>=B&&(K=0);var er=J-1;er<0&&(er=B-1);var nr,or=0,Q=H[J];if(Q!=-1){var tr=H[er];Q==tr?or=2:(nr=H[K],Q==nr&&(or=1))}if(0==or)rr.push(V[J]);else if(1==or){var sr=V[J],ir=V[K],N=G[J],ar=sr[0],vr=ir[0],ur=vr!=ar;if(ur){for(var fr=r.vor_positions.length,lr=sr[2],E=sr[3],pr=ir[4],z=ir[5],dr=void 0,gr=void 0,hr=0;hr<B;hr++){for(var cr=v(V[hr][3],N),yr=hr-J;yr<0;)yr+=B;for(;yr>=B;)yr-=B;yr<=2?void 0==dr?dr=cr:cr<dr&&(dr=cr):void 0==gr?gr=cr:cr<gr&&(gr=cr)}ur=dr<gr}if(ur){var D=y(z,E),T=[fr,N,lr,E,pr,z,D];rr.push(T),r.vor_positions.push(N);var _r=sr[4];r.vor_edges.push([ar,fr]),r.vor_edges.push([vr,fr]),r.vor_polygons[_r].boundary.shift();var br=r.vor_polygons[_r].boundary.length;r.vor_polygons[_r].boundary[br-1]=fr,r.vor_polygons[lr].boundary[1]==ar?(r.vor_polygons[lr].boundary.unshift(-1),r.vor_polygons[lr].boundary[1]=fr):(br=r.vor_polygons[lr].boundary.length,r.vor_polygons[lr].boundary[br-2]==ar&&(r.vor_polygons[lr].boundary.push(-1),br=r.vor_polygons[lr].boundary.length,r.vor_polygons[lr].boundary[br-2]=fr)),r.vor_polygons[pr].boundary[1]==vr?(r.vor_polygons[pr].boundary.unshift(-1),r.vor_polygons[pr].boundary[1]=fr):(br=r.vor_polygons[pr].boundary.length,r.vor_polygons[pr].boundary[br-2]==vr&&(r.vor_polygons[pr].boundary.push(-1),br=r.vor_polygons[pr].boundary.length,r.vor_polygons[pr].boundary[br-2]=fr))}else rr.push(sr),rr.push(ir)}}if(rr.length==V.length)break;V=rr}if(2==V.length){if(V[0][0]!=V[1][0]){for(var Ir=[],J=0;J<2;J++){var K=V[J][0];Ir.push(K),K=V[J][2],r.vor_polygons[K].boundary.shift(),r.vor_polygons[K].boundary.pop()}r.vor_edges.push(Ir)}}else if(3==V.length){var xr=V[0][0],wr=V[1][0],kr=V[2][0];if(xr!=wr&&xr!=kr&&wr!=kr){var fr=r.vor_positions.length,mr=V[0][3],Vr=V[1][3],Ar=V[2][3],N=d();h(N,i(mr,Vr)),h(N,i(Vr,Ar)),h(N,i(Ar,mr)),N=u(N),c(N,-1),r.vor_positions.push(N);for(var J=0;J<3;J++){var T=V[J];r.vor_edges.push([T[0],fr]);var s=T[2];r.vor_polygons[s].boundary.shift();var br=r.vor_polygons[s].boundary.length;r.vor_polygons[s].boundary[br-1]=fr}}}}for(var J=0;J<r.vor_polygons.length;J++)a=r.vor_polygons[J],a.boundary.length>=3&&a.boundary[0]>=0&&(n=new w(r.vor_positions,a.boundary.slice(0,3)),n.IsVertexOrderCorrect()||a.boundary.reverse())}else if(2==r.hull.length){var mr=r.positions[r.hull[0]],Vr=r.positions[r.hull[1]],E=d();h(E,mr),h(E,Vr),E=u(E),r.vor_positions.push(E);var z=u(i(mr,Vr));r.vor_positions.push(z);var Pr=g(E);c(Pr,-1),r.vor_positions.push(Pr);var qr=g(z);c(qr,-1),r.vor_positions.push(qr),r.vor_edges.push([0,1,2,3,0]),o=r.edges[0];for(var J=0;J<2;J++){var s=r.hull[J];r.vor_polygons[s]=new Object;var a=r.vor_polygons[s];a.edges=[o],a.triangles=[0],0==J?a.boundary=[0,1,2,3]:1==J&&(a.boundary=[0,3,2,1])}}}else if(3==r.hull.length){var n=r.triangles[0];r.vor_positions.push(n.ccdir);for(var J=0;J<3;J++){var K=J+1;K>=3&&(K=0);var er=J-1;er<0&&(er=2);var Vr=r.positions[r.hull[J]],Ar=r.positions[r.hull[K]],Cr=y(Ar,Vr);r.vor_positions.push(u(i(Cr,n.ccdir))),r.vor_edges.push([0,J+1,4]);var s=r.hull[J];r.vor_polygons[s]=new Object;for(var a=r.vor_polygons[s],Or=r.hull[er],Mr=0;Mr<3;Mr++){var o=r.edges[Mr],jr=P([Or,s],o.verts);if(2==jr.length)break}a.edges=[o],a.triangles=[n],a.boundary=[0,er+1,4,J+1]}var Lr=g(n.ccdir);c(Lr,-1),r.vor_positions.push(Lr)}}function F(r,e){var n=new Object;if(n.positions=r,n.indices=e,n.triangles=[],n.edges=[],n.hull=[],n.vor_positions=[],n.vor_edges=[],n.vor_polygons=new Object,e.length<3)return 2==e.length&&(n.edges.push(new k(e)),n.hull=e),S(n),n;var o=new w(r,e.slice(0,3));o.IsVertexOrderCorrect()||(o=new w(r,[e[0],e[2],e[1]])),n.triangles.push(o);for(var t=new Array(3),s=0;s<3;s++){var i=s+1;i>=3&&(i-=3);var a=e[s],v=e[i],u=[a,v],f=new k(u),l=new m(r,u);l.edge=f,t[s]=l,o.edges[s]=f,f.polys[0]=o,n.edges.push(f)}for(var p=e.slice(0,3),d=t,g=Object,s=0;s<3;s++){var i=s+2;i>=3&&(i-=3);var a=e[s];g[a]=[t[s],t[s+1]]}for(var h=3;h<e.length;h++){var a=e[h];if(!M(n,a)){g[a]=[];for(var c=[],y=[],b=[],I=0;I<p.length;I++){for(var v=p[I],x=new m(r,[a,v]),P=!1,F=0;F<d.length;F++){var E=d[F];if(P=x.intersects(r,E))break}if(!P){var f=new k(x.verts);x.edge=f,A(c,x),A(g[a],x),V(y,a),A(g[v],x),V(y,v)}}V(p,a);for(var I=0;I<d.length;I++){for(var x=d[I],z=[],D=0;D<2;D++){var T=q(g[a],g[x.verts[D]]);0!=T.length&&z.push(T[0])}if(!(z.length<2)){for(var B=-1,D=0;D<2;D++)if(_(x.edge.polys[D])){B=D;break}if(!(B<0)){var G=x.edge.polys[1-B],H=x.verts[0],J=G.VertexIndexIn(H),K=x.verts[1],N=G.VertexIndexIn(K),Q=N-J;if(Q<0&&(Q+=3),1==Q)var R=[a,K,H];else if(2==Q)var R=[a,H,K];var o=new w(r,R);if(o.IsVertexOrderCorrect()){n.triangles.push(o),x.edge.polys[B]=o,o.edges[0]=x.edge,o.edges[1]=z[0].edge,o.edges[2]=z[1].edge,A(b,x);for(var D=0;D<2;D++){var U=z[D];_(U.edge.polys[0])?(U.edge.polys[0]=o,n.edges.push(U.edge)):(U.edge.polys[1]=o,A(b,U))}}}}}for(var I=0;I<c.length;I++)A(d,c[I]);O(d,b);for(var W=[],I=0;I<y.length;I++){var X=y[I];O(g[X],b),0==g[X].length&&W.push(X)}C(p,W)}}return j(n),L(n),S(n),n}function E(r){for(var e=new Array(r.length),n=0;n<e.length;n++)e[n]=n;return F(r,e)}function z(){var r=Math.PI/180,s=function(e){var n=e[0]*r,o=e[1]*r,t=Math.cos(o);return[t*Math.cos(n),t*Math.sin(n),Math.sin(o)]},i=function(e){var n=Math.sqrt(e[0]*e[0]+e[1]*e[1]),o=Math.atan2(e[2],n),t=Math.atan2(e[1],e[0]);return[t/r,o/r]},a=function(r,e){return e.map(function(e){return i(r[e])})},v=t.voronoi()([]),u=v.DT=null,f=v.sites=[],l=v.pos=[],p=function(r){return"object"==typeof r&&"type"in r?d3.geoCentroid(r)[0]:0 in r?r[0]:void 0},d=function(r){return"object"==typeof r&&"type"in r?d3.geoCentroid(r)[1]:0 in r?r[1]:void 0},g=function(r){return v._hull=v._polygons=v._links=v._triangles=null,"object"==typeof r&&"FeatureCollection"==r.type&&(r=r.features),f=r.map(function(r,e){return r.index=e,r}),l=r.map(function(r){return[p(r),d(r)]}),u=E(l.map(s)),v};return v.links=g.links=function(r){if(r&&g(r),v._links)return v._links;var t=n.map(),s=u.edges.map(function(r,n){t.set(e.extent(r.verts),n);var s={source:f[r.verts[0]],target:f[r.verts[1]],urquhart:!0,length:o.geoLength({type:"LineString",coordinates:[l[r.verts[0]],l[r.verts[1]]]})};return{type:"LineString",coordinates:[i(u.positions[r.verts[0]]),i(u.positions[r.verts[1]])],properties:s}});return u.triangles.forEach(function(r){for(var n,o,i=0,a=0,v=0;v<3;v++){o=e.extent([r.verts[v],r.verts[(v+1)%3]]);var u=t.get(o);a=s[u].properties.length,a>i&&(i=a,n=u)}s[n].properties.urquhart=!1}),v._links={type:"FeatureCollection",features:s}},v.triangles=g.triangles=function(r){if(r&&g(r),v._triangles)return v._triangles;var e=u.triangles.map(function(r){return r.spherical=r.verts.map(function(r){return u.positions[r]}).map(i),r.ccdsq<0&&(r.spherical=r.spherical.reverse(),r.ccdsq*=-1),r}).map(function(r){return{type:"Polygon",coordinates:[r.spherical.concat([r.spherical[0]])],properties:{sites:r.verts.map(function(r){return f[r]}),area:r.vol,circumcenter:i(r.ccdir),circumradius:r.ccdsq}}});return v._triangles={type:"FeatureCollection",features:e}},v.polygons=g.polygons=function(r){if(r&&g(r),v._polygons)return v._polygons;var e=u.indices.map(function(r,e){var n={},t=u.vor_polygons[u.indices[r]];if(void 0==t)n.type="Sphere";else{var s=a(u.vor_positions,t.boundary.concat([t.boundary[0]])),i={type:"Polygon",coordinates:[[l[r],s[0],s[1],l[r]]]};o.geoArea(i)>2*Math.PI+1e-10&&(s=s.reverse()),n.type="Polygon",n.coordinates=[s]}return n.properties={site:f[r],sitecoordinates:l[r],neighbours:t.edges.map(function(e){return e.verts.filter(function(e){return e!==r})[0]})},n});return v._polygons={type:"FeatureCollection",features:e}},v.hull=g.hull=function(r){if(r&&g(r),v._hull)return v._hull;if(!u.hull.length)return null;var e=u.hull.reverse();return v._hull={type:"Polygon",coordinates:[e.concat([e[0]]).map(function(r){return l[r]})],properties:{sites:e.map(function(r){return f[r]})}}},v.find=function(r,e,n){var o,t=v.polygons().features,s=v.find.found||0,i=t[s]||t[s=0],a=d3.geoLength({type:"LineString",coordinates:[[r,e],i.properties.sitecoordinates]});do i=t[o=s],s=null,i.properties.neighbours.forEach(function(n){var o=d3.geoLength({type:"LineString",coordinates:[[r,e],t[n].properties.sitecoordinates]});if(o<a)return a=o,void(s=n)});while(null!==s);if(v.find.found=o,!n||a<n*n)return i.properties.site},g.x=function(r){return r?(p=r,g):p},g.y=function(r){return r?(d=r,g):d},g.extent=function(r){return r?g:null},g.size=function(r){return r?g:null},g}w.prototype.copy_vert_info=function(r){this.verts=r.verts,this.dirs=r.dirs,this.vol=r.vol,this.ccdir=r.ccdir,this.ccdsq=r.ccdsq},w.prototype.IsVertexOrderCorrect=function(){return this.vol>=0},w.prototype.IsPointInside=function(r){for(var e=0;e<3;e++)if(s(r,this.dirs[e])<0)return!1;return!0},w.prototype.IsPointInCircumcircle=function(r){return v(this.ccdir,r)<this.ccdsq},w.prototype.IsVertex=function(r){for(var e=0;e<3;e++)if(r==this.verts[e])return!0;return!1},w.prototype.VertexIndexIn=function(r){for(var e=0;e<3;e++)if(r==this.verts[e])return e;return-1},w.prototype.EdgeIndexIn=function(r){for(var e=0;e<3;e++)if(I(this.edges[e],r))return e;return-1},k.prototype.IsVertex=function(r){for(var e=0;e<2;e++)if(r==this.verts[e])return!0;return!1},k.prototype.VertexIndexIn=function(r){for(var e=0;e<2;e++)if(r==this.verts[e])return e;return-1},k.prototype.PolyIndexIn=function(r){for(var e=0;e<2;e++)if(b(this.polys[e],r))return e;return-1},m.prototype.intersects=function(r,e){for(var n=0;n<2;n++)for(var o=0;o<2;o++)if(this.verts[n]==e.verts[o])return!1;var t=u(i(this.direc,e.direc)),a=s(t,this.midpnt)>0,f=s(t,e.midpnt)>0;if(f!=a)return!1;for(var l=[],n=0;n<2;n++){var p=v(t,r[this.verts[n]]);l.push(p)}for(var d=[],n=0;n<2;n++){var p=v(t,r[e.verts[n]]);d.push(p)}var g=x(l[0],l[1]),h=x(d[0],d[1]);if(g<=this.pdst&&h<=e.pdst&&a)return!0;c(t,-1),a=!a;for(var n=0;n<2;n++)l[n]=-l[n],d[n]=-d[n];return g=x(l[0],l[1]),h=x(d[0],d[1]),!!(g<=this.pdst&&h<=e.pdst&&a)},r.geoVoronoi=z,Object.defineProperty(r,"__esModule",{value:!0})});
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
<!DOCTYPE html>
<meta charset="utf-8">
<script src="../d3-geo-voronoi/dev/d3.v4.js"></script>
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="d3-geo-voronoi.min.js"></script>
<!--
*Note to self: do not edit this block on blockbuilder*
-->
<style>
body {
margin: 0;
overflow: hidden;
background: black;
font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif;
}
canvas {
cursor: move;
}
svg, canvas, #legend {
position:absolute;
top:0;
}
.missions path { fill: white }
.polygons path {
fill: rgba(0,0,0,0.001); /* to receive mouseover events */
stroke: rgba(200,255,220,0.1);
stroke-width: 1.5;
}
.links path {
fill: none;
stroke: rgba(180,90,90,0.9);
stroke-width: 3;
}
path.glow {
fill: none;
stroke: rgba(255,255,255,0.7);
stroke-width: 1;
}
#legend {
color: white;
top: 1em;
left: 1em;
padding: 0.5em 1em;
border-left: solid white 4px;
background: rgba(255,255,255,0.1);
width: 19em;
line-height: 1.4em;
}
#legend a {
color: white;
}
</style>
<canvas></canvas>
<svg>
<defs>
<radialGradient id="grad1" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:black;stop-opacity:0" />
<stop offset="94%" style="stop-color:black;stop-opacity:0.01" />
<stop offset="97%" style="stop-color:black;stop-opacity:0.2" />
<stop offset="110%" style="stop-color:black;stop-opacity:0.6" />
</radialGradient>
</defs>
</svg>
<div id="legend">
<h1>Missions to Mars</h1>
<p>A d3-geo-voronoi demo<br>by <a href="http://bl.ocks.org/Fil/" target="_blank">Philippe Rivière</a>.</p>
</div>
<script id="vertex-shader" type="x-shader/x-vertex">
attribute vec2 a_position;
void main(void) {
gl_Position = vec4(a_position, 0.0, 1.0);
}
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_translate; /* width/2, height/2 */
uniform float u_scale; /* in pixels ! */
uniform vec3 u_rotate; /* rotation in degrees ! */
const float c_pi = 3.14159265358979323846264;
const float c_halfPi = c_pi * 0.5;
const float c_twoPi = c_pi * 2.0;
// Inclination of the equator on Mars = 25.19° (earth= 23.44)
const float declination = 25.19 / 90.0 * c_halfPi;
float phi0 = -u_rotate.y / 90.0 * c_halfPi;
float cosphi0 = cos(phi0);
float sinphi0 = sin(phi0);
void main(void) {
float x = (gl_FragCoord.x - u_translate.x) / u_scale;
float y = (u_translate.y - gl_FragCoord.y) / u_scale;
// inverse orthographic projection
float rho = sqrt(x * x + y * y);
// color if the point (px, py) does not exist in the texture
if (rho >= 1.0) {
gl_FragColor = texture2D(u_image, vec2(0.0, 0.0));
gl_FragColor[0] = 0.1*(rho-1.0+0.1);
gl_FragColor[1] = 0.06*(rho-1.0+0.1);
gl_FragColor[2] = 0.2*(rho-1.0+0.1);
}
else {
float c = asin(rho);
float sinc = sin(c);
float cosc = cos(c);
float lambda = atan(x * sinc, rho * cosc);
float phi = asin(y * sinc / rho);
// inverse rotation
float cosphi = cos(phi);
float x0 = cos(lambda) * cosphi;
float y0 = sin(lambda) * cosphi;
float cosgamma = cos(u_rotate.z / 90.0 * c_halfPi);
float singamma = sin(u_rotate.z / 90.0 * c_halfPi);
float x1 = x0 * cosgamma - y0 * singamma;
float y1 = y0 * cosgamma + x0 * singamma;
float z1 = y * sinc / rho;
lambda = atan(y1, x1 * cosphi0 + z1 * sinphi0) - u_rotate.x / 90.0 * c_halfPi;
phi = asin(z1 * cosphi0 - x1 * sinphi0);
// pixels
float px = (lambda + c_pi) / c_twoPi;
float py = (phi + c_halfPi) / c_pi;
gl_FragColor = texture2D(u_image, vec2(px, py));
// terminator ?? see https://github.com/joergdietrich/Leaflet.Terminator/blob/master/L.Terminator.js
// float sinh = sin(lambda)*sin(declination) + cos(lambda)*cos(declination)*cos(1.0);
// float intensity = (sinh > 0.0) ? 1.0 + 0.1*sinh : 0.2 + 0.8 * exp(6.0*sinh);
float intensity = 1.1; // boost the pixel by some factor
gl_FragColor[0] = intensity * gl_FragColor[0] * (1.3 - 0.3 * sqrt(gl_FragColor[0]));
gl_FragColor[1] = intensity * gl_FragColor[1];
gl_FragColor[2] = intensity * gl_FragColor[2];
}
}
</script>
<script>
// Select the canvas from the document.
var canvas = document.querySelector("canvas");
var width = +canvas.getAttribute('width') || 600,
height = +canvas.getAttribute('height') || 400;
width = Math.max(width, self.innerWidth);
height = Math.max(height, self.innerHeight);
// save the legend
var legend = d3.select('#legend').html();
// Create the WebGL context, with fallback for experimental support.
var context = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
// Compile the vertex shader.
var vertexShader = context.createShader(context.VERTEX_SHADER);
context.shaderSource(vertexShader, document.querySelector("#vertex-shader").textContent);
context.compileShader(vertexShader);
if (!context.getShaderParameter(vertexShader, context.COMPILE_STATUS)) throw new Error(context.getShaderInfoLog(vertexShader));
// Compile the fragment shader.
var fragmentShader = context.createShader(context.FRAGMENT_SHADER);
context.shaderSource(fragmentShader, document.querySelector("#fragment-shader").textContent);
context.compileShader(fragmentShader);
if (!context.getShaderParameter(fragmentShader, context.COMPILE_STATUS)) throw new Error(context.getShaderInfoLog(fragmentShader));
// Link and use the program.
var program = context.createProgram();
context.attachShader(program, vertexShader);
context.attachShader(program, fragmentShader);
context.linkProgram(program);
if (!context.getProgramParameter(program, context.LINK_STATUS)) throw new Error(context.getProgramInfoLog(program));
context.useProgram(program);
// Define the positions (as vec2) of the square that covers the canvas.
var positionBuffer = context.createBuffer();
context.bindBuffer(context.ARRAY_BUFFER, positionBuffer);
context.bufferData(context.ARRAY_BUFFER, new Float32Array([
-1.0, -1.0,
+1.0, -1.0,
+1.0, +1.0,
-1.0, +1.0
]), context.STATIC_DRAW);
// Bind the position buffer to the position attribute.
var positionAttribute = context.getAttribLocation(program, "a_position");
context.enableVertexAttribArray(positionAttribute);
context.vertexAttribPointer(positionAttribute, 2, context.FLOAT, false, 0, 0);
// Extract the projection parameters.
var translateUniform = context.getUniformLocation(program, "u_translate"),
scaleUniform = context.getUniformLocation(program, "u_scale"),
rotateUniform = context.getUniformLocation(program, "u_rotate");
// Load the reference image.
var image = new Image;
image.src = "Mars_Viking_MDIM21_ClrMosaic_global_1024.jpg";
image.onload = readySoon;
var projection = d3.geoOrthographic()
.translate([width / 2, height / 2])
.scale(0.95 * height / 2);
var path = d3.geoPath()
.projection(projection);
var svg = d3.select('svg')
.attr('width', width)
.attr('height', height);
svg._defs = svg.append("defs");
svg._clip = svg._defs.append("path")
.datum({
type: "Sphere"
})
.attr("id", "sphere");
svg._defs.append("clipPath")
.attr("id", "clip")
.append("use")
.attr("xlink:href", "#sphere");
svg._earth = svg.append('g')
.attr("clip-path", "url(#clip)")
.style('cursor', '-webkit-grab');
svg._polygons = svg._earth.append('g').attr('class', 'polygons');
svg._links = svg._earth.append('g').attr('class', 'links');
svg._missions = svg._earth.append('g').attr('class', 'missions');
svg._shade = svg.append("use")
.attr("class", "stroke")
.attr("xlink:href", "#sphere")
.attr("stroke", "black")
.attr("stroke-width", 3)
.attr("fill", "url(#grad1)")
.attr('pointer-events', 'none');
// Hack to ensure correct inference of window dimensions.
function readySoon() {
// https://raw.githubusercontent.com/rhodges/hodgimoto/master/app/layers/mars_landings.geojson
d3.json('mars_landings.geojson', function (err, missions) {
missions.features.push({"type":"Feature","properties":{"OBJECTID":-1,"ID":-1,"NAME":"Schiaparelli","X_COORD":0.2,"Y_COORD":357.5,"FULL_NAME":"ExoMars Schiaparelli EDM lander","NSSDC_ID":"2016-017A","WEB_LINK":"https://en.wikipedia.org/wiki/Schiaparelli_EDM_lander","COUNTRY":"Europe","YEAR":2016},"geometry":{"type":"MultiPoint","coordinates":[[0.2, 357.5]]}})
var v = svg._voronoi = d3.geoVoronoi()(missions),
polygons = v.polygons(),
urquhart = v.links().features.filter(function(l) {
return l.properties.urquhart;
});
svg._missions = svg._missions
.selectAll('path')
.data(missions.features);
var enter = svg._missions.enter().append('path');
svg._missions = svg._missions.merge(enter);
svg._polygons = svg._polygons
.selectAll('path')
.data(polygons.features);
var enter = svg._polygons.enter().append('path');
svg._polygons = svg._polygons.merge(enter);
svg._links = svg._links
.selectAll('path')
.data(urquhart);
var enter = svg._links.enter().append('path');
svg._links = svg._links.merge(enter);
setTimeout(function () {
resize();
ready();
}, 10);
});
}
// retina display
var devicePixelRatio = window.devicePixelRatio || 1;
function resize() {
canvas.setAttribute('width', width * devicePixelRatio);
canvas.setAttribute('height', height * devicePixelRatio);
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
context.uniform2f(translateUniform, width / 2 * devicePixelRatio, height / 2 * devicePixelRatio);
context.viewport(0, 0, width * devicePixelRatio, height * devicePixelRatio);
}
function ready() {
// Create a texture and a mipmap for accurate minification.
var texture = context.createTexture();
context.bindTexture(context.TEXTURE_2D, texture);
context.texParameteri(context.TEXTURE_2D, context.TEXTURE_MAG_FILTER, context.LINEAR);
context.texParameteri(context.TEXTURE_2D, context.TEXTURE_MIN_FILTER, context.LINEAR_MIPMAP_LINEAR);
context.texImage2D(context.TEXTURE_2D, 0, context.RGBA, context.RGBA, context.UNSIGNED_BYTE, image);
context.generateMipmap(context.TEXTURE_2D);
// The current rotation
var scale = scale0 = projection.scale(),
rotate = [0, 0, 0];
// Rotate and redraw!
function redraw() {
projection.scale(scale).rotate(rotate);
svg._missions.attr('d', path);
svg._polygons.attr('d', path);
svg._links.attr('d', path);
svg._clip.attr('d', path);
context.uniform1f(scaleUniform, scale * devicePixelRatio);
context.uniform3fv(rotateUniform, rotate);
context.bindTexture(context.TEXTURE_2D, texture); // XXX Safari
context.drawArrays(context.TRIANGLE_FAN, 0, 4);
}
svg
.on('mousemove', function () {
var p = d3.mouse(this),
c = projection.invert(p),
found;
// if we're on the Earth
if (c[0] !== 90 &&
(found = svg._voronoi.find(c[0], c[1], 0.8 /* radian */ ))) {
var center = d3.geoCentroid(svg._missions.data()[found.index]);
var circle = d3.geoCircle().center(center),
r = d3.geoLength({
type: "LineString",
coordinates: [c, center]
}) * 180 / Math.PI;
r = Math.max(1.1 * r, 5);
svg._earth.append('path')
.attr('class', 'glow')
.transition()
.attrTween('d', function () {
return function (t) {
return path(circle.radius(2 + r * d3.easePolyIn(t, 4))()) || ''; // empty path when the signal comes from the hidden side of the planet
};
})
.remove();
var p = svg._missions.data()[found.index].properties;
d3.select('#legend h1').text(p.NAME);
d3.select('#legend p').html(
'<a href="' + p.WEB_LINK + '">' + p.FULL_NAME + '</a>' +
'<br>' +
p.COUNTRY + ', ' + p.YEAR + '.'
/* +
'<br>' +
p.X_COORD + "&times" + p.Y_COORD */
);
path.pointRadius(function (d, j) {
return j == found.index ? 8 : 4.5 /* 4.5 = default value */ ;
});
redraw();
} else {
d3.select('#legend').html(legend);
}
});
var lambda = d3.scaleLinear()
.domain([-width / 2, width / 2])
.range([-180, 180]);
var phi = d3.scaleLinear()
.domain([0, height])
.range([90, -90]);
var q, r, transform, d;
zoom = d3.zoom()
.scaleExtent([.8, 1.5])
.on("start", function () {
q = rotate, d = [0, 0, 0]; // accumulate change in d
r = d3.mouse(this);
svg._earth.style('cursor', '-webkit-grabbing');
})
.on("zoom.redraw", function () {
scale = scale0 * d3.event.transform.k;
var p = d3.mouse(this);
var dr = [lambda(p[0]) - lambda(r[0]), phi(p[1]) - phi(r[1])];
r = p;
// inverse dr[0] if the mouse is beyond one of the poles
var a = (phi(p[1]) - rotate[1]) * Math.PI / 180,
ca = Math.cos(a),
sa = Math.sin(a);
d = [d[0] + dr[0] * (ca < 0 ? -1 : 1),
d[1] + dr[1], d[2] + dr[0] * -sa];
rotate = [q[0] + d[0], q[1] + d[1], q[2] + 0 * d[2]];
redraw();
})
.on('end', function() {
svg._earth.style('cursor', '-webkit-grab');
});
d3.select("svg")
.call(zoom);
redraw();
var elapsed = null;
function animate(t) {
elapsed = t;
//d3.select("canvas").transition().call(zoom.transform, d3.zoomIdentity);
requestAnimationFrame(animate);
}
//animate();
}
// A polyfill for requestAnimationFrame.
if (!self.requestAnimationFrame) requestAnimationFrame =
self.webkitRequestAnimationFrame || self.mozRequestAnimationFrame || self.msRequestAnimationFrame || self.oRequestAnimationFrame || function (f) {
setTimeout(f, 17);
};
</script>
Display the source blob
Display the rendered blob
Raw
{
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "properties": { "OBJECTID": 1, "ID": 1, "NAME": "Mars 2", "X_COORD": -158.0943, "Y_COORD": -45.0419, "FULL_NAME": "Mars 2 Landing Site", "NSSDC_ID": "1971-045A", "WEB_LINK": "http:\/\/nssdc.gsfc.nasa.gov\/database\/MasterCatalog?sc=1971-045A", "COUNTRY": "Russian", "YEAR": 1971 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -158.094299975986871, -45.041900050608 ] ] } },
{ "type": "Feature", "properties": { "OBJECTID": 2, "ID": 2, "NAME": "Mars 6", "X_COORD": -19.4528, "Y_COORD": -23.9203, "FULL_NAME": "Mars 6 Landing Site", "NSSDC_ID": "1973-052A", "WEB_LINK": "http:\/\/nssdc.gsfc.nasa.gov\/database\/MasterCatalog?sc=1973-052A", "COUNTRY": "Russian", "YEAR": 1973 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -19.452799942064459, -23.920299959256187 ] ] } },
{ "type": "Feature", "properties": { "OBJECTID": 3, "ID": 3, "NAME": "Viking 1", "X_COORD": -47.9507, "Y_COORD": 22.45709, "FULL_NAME": "Viking 1 Landing Site", "NSSDC_ID": "1975-075C", "WEB_LINK": "http:\/\/nssdc.gsfc.nasa.gov\/database\/MasterCatalog?sc=1975-075C", "COUNTRY": "American", "YEAR": 1975 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -47.95070005550032, 22.457088414449796 ] ] } },
{ "type": "Feature", "properties": { "OBJECTID": 4, "ID": 4, "NAME": "Viking 2", "X_COORD": 133.739, "Y_COORD": 47.93254, "FULL_NAME": "Viking 2 Landing Site", "NSSDC_ID": "1975-083C", "WEB_LINK": "http:\/\/nssdc.gsfc.nasa.gov\/database\/MasterCatalog?sc=1975-083C", "COUNTRY": "American", "YEAR": 1975 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ 133.738999997801329, 47.9325413832237 ] ] } },
{ "type": "Feature", "properties": { "OBJECTID": 5, "ID": 5, "NAME": "Pathfinder", "X_COORD": -33.2528, "Y_COORD": 19.26077, "FULL_NAME": "Mars Pathfinder Landing Site", "NSSDC_ID": "1996-068A", "WEB_LINK": "http:\/\/nssdc.gsfc.nasa.gov\/database\/MasterCatalog?sc=1996-068A", "COUNTRY": "American", "YEAR": 1996 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -33.252799950643755, 19.260767168830739 ] ] } },
{ "type": "Feature", "properties": { "OBJECTID": 6, "ID": 6, "NAME": "MER A", "X_COORD": 175.4729, "Y_COORD": -14.5692, "FULL_NAME": "MER Spirit Landing Site", "NSSDC_ID": "2003-027A", "WEB_LINK": "http:\/\/nssdc.gsfc.nasa.gov\/database\/MasterCatalog?sc=2003-027A", "COUNTRY": "American", "YEAR": 2003 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ 175.472900043565204, -14.569200049607367 ] ] } },
{ "type": "Feature", "properties": { "OBJECTID": 7, "ID": 7, "NAME": "MER B", "X_COORD": -5.5266, "Y_COORD": -1.9462, "FULL_NAME": "MER Opportunity Landing Site", "NSSDC_ID": "2003-032A", "WEB_LINK": "http:\/\/nssdc.gsfc.nasa.gov\/database\/MasterCatalog?sc=2003-032A", "COUNTRY": "American", "YEAR": 2003 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -5.526599940193367, -1.946199938475814 ] ] } },
{ "type": "Feature", "properties": { "OBJECTID": 8, "ID": 8, "NAME": "PHX", "X_COORD": -125.74922, "Y_COORD": 68.21883, "FULL_NAME": "Phoenix Landing Site", "NSSDC_ID": "n\/a", "WEB_LINK": "http:\/\/www.nasa.gov\/mission_pages\/phoenix\/main", "COUNTRY": "American", "YEAR": 2008 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -125.74922203747397, 68.218830004033862 ] ] } },
{ "type": "Feature", "properties": { "OBJECTID": 9, "ID": 0, "NAME": "MSL", "X_COORD": 137.44, "Y_COORD": -4.59, "FULL_NAME": "MSL Curiosity Landing Site (prelim)", "NSSDC_ID": "n\/a", "WEB_LINK": "http:\/\/www.nasa.gov\/mission_pages\/msl\/", "COUNTRY": "American", "YEAR": 2012 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ 137.440000000251104, -4.589999999951574 ] ] } }
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment