Skip to content

Commit bd585e2

Browse files
committed
Add d3.geo.circle.
There is also a greatCircle as an alias, whose angle defaults to 90 degrees. (Or should, but some projections cannot handle 90 degrees so we use 89 instead.)
1 parent b552513 commit bd585e2

5 files changed

Lines changed: 261 additions & 244 deletions

File tree

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ d3.geo.js: \
188188
src/geo/type.js \
189189
src/geo/path.js \
190190
src/geo/bounds.js \
191+
src/geo/circle.js \
191192
src/geo/greatArc.js \
192193
src/geo/greatCircle.js \
193194
src/end.js

d3.geo.js

Lines changed: 102 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -629,95 +629,16 @@ function d3_geo_boundsPolygon(o, f) {
629629
f.apply(null, a[i]);
630630
}
631631
}
632-
d3.geo.greatArc = function() {
633-
var source = d3_geo_greatArcSource,
634-
target = d3_geo_greatArcTarget,
635-
precision = 6 * d3_geo_radians;
636-
637-
function greatArc() {
638-
var a = typeof source === "function" ? source.apply(this, arguments) : source,
639-
b = typeof target === "function" ? target.apply(this, arguments) : target,
640-
i = d3_geo_greatArcInterpolate(a, b),
641-
dt = precision / i.d,
642-
t = 0,
643-
coordinates = [a];
644-
while ((t += dt) < 1) coordinates.push(i(t));
645-
coordinates.push(b);
646-
return {
647-
type: "LineString",
648-
coordinates: coordinates
649-
};
650-
}
651-
652-
// Length returned in radians; multiply by radius for distance.
653-
greatArc.distance = function() {
654-
var a = typeof source === "function" ? source.apply(this, arguments) : source,
655-
b = typeof target === "function" ? target.apply(this, arguments) : target;
656-
return d3_geo_greatArcInterpolate(a, b).d;
657-
};
658-
659-
greatArc.source = function(x) {
660-
if (!arguments.length) return source;
661-
source = x;
662-
return greatArc;
663-
};
664-
665-
greatArc.target = function(x) {
666-
if (!arguments.length) return target;
667-
target = x;
668-
return greatArc;
669-
};
670-
671-
// Precision is specified in degrees.
672-
greatArc.precision = function(x) {
673-
if (!arguments.length) return precision / d3_geo_radians;
674-
precision = x * d3_geo_radians;
675-
return greatArc;
676-
};
677-
678-
return greatArc;
679-
};
680-
681-
function d3_geo_greatArcSource(d) {
682-
return d.source;
683-
}
684-
685-
function d3_geo_greatArcTarget(d) {
686-
return d.target;
687-
}
688-
689-
function d3_geo_greatArcInterpolate(a, b) {
690-
var x0 = a[0] * d3_geo_radians, cx0 = Math.cos(x0), sx0 = Math.sin(x0),
691-
y0 = a[1] * d3_geo_radians, cy0 = Math.cos(y0), sy0 = Math.sin(y0),
692-
x1 = b[0] * d3_geo_radians, cx1 = Math.cos(x1), sx1 = Math.sin(x1),
693-
y1 = b[1] * d3_geo_radians, cy1 = Math.cos(y1), sy1 = Math.sin(y1),
694-
d = interpolate.d = Math.acos(Math.max(-1, Math.min(1, sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)))),
695-
sd = Math.sin(d);
696-
697-
// From http://williams.best.vwh.net/avform.htm#Intermediate
698-
function interpolate(t) {
699-
var A = Math.sin(d - (t *= d)) / sd,
700-
B = Math.sin(t) / sd,
701-
x = A * cy0 * cx0 + B * cy1 * cx1,
702-
y = A * cy0 * sx0 + B * cy1 * sx1,
703-
z = A * sy0 + B * sy1;
704-
return [
705-
Math.atan2(y, x) / d3_geo_radians,
706-
Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_geo_radians
707-
];
708-
}
709-
710-
return interpolate;
711-
}
712632
// TODO breakAtDateLine?
633+
// TODO use 90 degrees rather than 89 degrees
713634

714-
d3.geo.greatCircle = function() {
635+
d3.geo.circle = function() {
715636
var origin = [0, 0],
716637
degrees = 89,
717-
radius = degrees * d3_geo_radians,
638+
radians = degrees * d3_geo_radians,
718639
arc = d3.geo.greatArc().target(Object);
719640

720-
function greatCircle() {
641+
function circle() {
721642
// TODO
722643
// var o = typeof origin === "function" ? origin.apply(this, arguments) : origin;
723644
// return d3_geo_greatCircleArc({
@@ -729,10 +650,10 @@ d3.geo.greatCircle = function() {
729650
// TODO
730651
//
731652
// function visible(point) {
732-
// return arc.distance(point) < radius;
653+
// return arc.distance(point) < radians;
733654
// }
734655

735-
greatCircle.clip = function(d) {
656+
circle.clip = function(d) {
736657
arc.source(typeof origin === "function" ? origin.apply(this, arguments) : origin);
737658
return clipType(d);
738659
};
@@ -802,14 +723,14 @@ d3.geo.greatCircle = function() {
802723

803724
while (++i < n) {
804725
d1 = arc.distance(p2 = coordinates[i]);
805-
if (d1 < radius) {
806-
if (p1) clipped.push(d3_geo_greatArcInterpolate(p1, p2)((d0 - radius) / (d0 - d1)));
726+
if (d1 < radians) {
727+
if (p1) clipped.push(d3_geo_greatArcInterpolate(p1, p2)((d0 - radians) / (d0 - d1)));
807728
clipped.push(p2);
808729
p0 = p1 = null;
809730
} else {
810731
p1 = p2;
811732
if (!p0 && clipped.length) {
812-
clipped.push(d3_geo_greatArcInterpolate(clipped[clipped.length - 1], p1)((radius - d0) / (d1 - d0)));
733+
clipped.push(d3_geo_greatArcInterpolate(clipped[clipped.length - 1], p1)((radians - d0) / (d1 - d0)));
813734
p0 = p1;
814735
}
815736
}
@@ -818,7 +739,7 @@ d3.geo.greatCircle = function() {
818739

819740
if (p1 && clipped.length) {
820741
d1 = arc.distance(p2 = clipped[0]);
821-
clipped.push(d3_geo_greatArcInterpolate(p1, p2)((d0 - radius) / (d0 - d1)));
742+
clipped.push(d3_geo_greatArcInterpolate(p1, p2)((d0 - radians) / (d0 - d1)));
822743
}
823744

824745
return resample(clipped);
@@ -843,19 +764,106 @@ d3.geo.greatCircle = function() {
843764
return resampled;
844765
}
845766

846-
greatCircle.origin = function(x) {
767+
circle.origin = function(x) {
847768
if (!arguments.length) return origin;
848769
origin = x;
849-
return greatCircle;
770+
return circle;
771+
};
772+
773+
circle.angle = function(x) {
774+
if (!arguments.length) return degrees;
775+
degrees = +x;
776+
return circle;
850777
};
851778

852779
// Precision is specified in degrees.
853-
greatCircle.precision = function(x) {
780+
circle.precision = function(x) {
854781
if (!arguments.length) return arc.precision();
855782
arc.precision(x);
856-
return greatCircle;
783+
return circle;
784+
};
785+
786+
return circle;
787+
}
788+
d3.geo.greatArc = function() {
789+
var source = d3_geo_greatArcSource,
790+
target = d3_geo_greatArcTarget,
791+
precision = 6 * d3_geo_radians;
792+
793+
function greatArc() {
794+
var a = typeof source === "function" ? source.apply(this, arguments) : source,
795+
b = typeof target === "function" ? target.apply(this, arguments) : target,
796+
i = d3_geo_greatArcInterpolate(a, b),
797+
dt = precision / i.d,
798+
t = 0,
799+
coordinates = [a];
800+
while ((t += dt) < 1) coordinates.push(i(t));
801+
coordinates.push(b);
802+
return {
803+
type: "LineString",
804+
coordinates: coordinates
805+
};
806+
}
807+
808+
// Length returned in radians; multiply by radius for distance.
809+
greatArc.distance = function() {
810+
var a = typeof source === "function" ? source.apply(this, arguments) : source,
811+
b = typeof target === "function" ? target.apply(this, arguments) : target;
812+
return d3_geo_greatArcInterpolate(a, b).d;
813+
};
814+
815+
greatArc.source = function(x) {
816+
if (!arguments.length) return source;
817+
source = x;
818+
return greatArc;
819+
};
820+
821+
greatArc.target = function(x) {
822+
if (!arguments.length) return target;
823+
target = x;
824+
return greatArc;
857825
};
858826

859-
return greatCircle;
827+
// Precision is specified in degrees.
828+
greatArc.precision = function(x) {
829+
if (!arguments.length) return precision / d3_geo_radians;
830+
precision = x * d3_geo_radians;
831+
return greatArc;
832+
};
833+
834+
return greatArc;
835+
};
836+
837+
function d3_geo_greatArcSource(d) {
838+
return d.source;
839+
}
840+
841+
function d3_geo_greatArcTarget(d) {
842+
return d.target;
843+
}
844+
845+
function d3_geo_greatArcInterpolate(a, b) {
846+
var x0 = a[0] * d3_geo_radians, cx0 = Math.cos(x0), sx0 = Math.sin(x0),
847+
y0 = a[1] * d3_geo_radians, cy0 = Math.cos(y0), sy0 = Math.sin(y0),
848+
x1 = b[0] * d3_geo_radians, cx1 = Math.cos(x1), sx1 = Math.sin(x1),
849+
y1 = b[1] * d3_geo_radians, cy1 = Math.cos(y1), sy1 = Math.sin(y1),
850+
d = interpolate.d = Math.acos(Math.max(-1, Math.min(1, sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)))),
851+
sd = Math.sin(d);
852+
853+
// From http://williams.best.vwh.net/avform.htm#Intermediate
854+
function interpolate(t) {
855+
var A = Math.sin(d - (t *= d)) / sd,
856+
B = Math.sin(t) / sd,
857+
x = A * cy0 * cx0 + B * cy1 * cx1,
858+
y = A * cy0 * sx0 + B * cy1 * sx1,
859+
z = A * sy0 + B * sy1;
860+
return [
861+
Math.atan2(y, x) / d3_geo_radians,
862+
Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_geo_radians
863+
];
864+
}
865+
866+
return interpolate;
860867
}
868+
d3.geo.greatCircle = d3.geo.circle;
861869
})();

0 commit comments

Comments
 (0)