You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

255 lines
8.6 KiB

(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(factory((global.L = global.L || {}, global.L.Wrapped = {})));
}(this, (function (exports) { 'use strict';
var version = "1.0.0+master.0861cee";
/*
* @namespace L.Wrapped
* Utility functions to calculate various aspects of linear geometry to
* calculate if lines should be wrapped as well as common functionality used by both Polygons and Polylines.
*/
// @function calculateAntimeridianLat (latLngA: LatLng, latLngB: latLng)
// Returns the calculated latitude where a line drawn between
// two Latitude/Longitude points will cross the antimeridian.
function calculateAntimeridianLat(latLngA, latLngB) {
// Ensure that the latitude A is less than latidue B. This will allow the
// crossing point to be calculated based on the purportional similarity of
// right triangles.
if (latLngA.lat > latLngB.lat) {
var temp = latLngA;
latLngA = latLngB;
latLngB = temp;
}
var A = 360 - Math.abs(latLngA.lng - latLngB.lng);
var B = latLngB.lat - latLngA.lat;
var a = Math.abs(180 - Math.abs(latLngA.lng));
return latLngA.lat + ((B * a) / A);
}
// @function isCrossAntimeridian(latLngA: LatLng, latLngB: LatLng)
// Returns true if the line between the two points will cross either
// the prime meridian (Greenwich) or its antimeridian (International Date Line)
function isCrossMeridian(latLngA, latLngB) {
// Returns true if the signs are not the same.
return sign(latLngA.lng) * sign(latLngB.lng) < 0;
}
// @function sign(Number)
// Returns NaN for non-numbers, 0 for 0, -1 for negative numbers,
// 1 for positive numbers
function sign(x) {
return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : 0 : NaN;
}
// @function pushLatLng(ring: Point[], projectedBounds: LatLngBounds, latlng: LatLng, map: Map)
// Adds the latlng to the current ring as a layer point and expands the projected bounds.
function pushLatLng(ring, projectedBounds, latlng, map) {
ring.push(map.latLngToLayerPoint(latlng));
projectedBounds.extend(ring[ring.length - 1]);
}
// @function isBreakRing(latLngA: LatLng, latLngB: LatLng)
// Determines when the ring should be broken and a new one started.
function isBreakRing(latLngA, latLngB) {
return isCrossMeridian(latLngA, latLngB) &&
Math.abs(latLngA.lng) > 90 &&
Math.abs(latLngB.lng) > 90;
}
// @function breakRing(currentLat: LatLng, nextLat: LatLng, rings: Point[][],
// projectedBounds: LatLngBounds, map: Map)
// Breaks the existing ring along the anti-meridian.
// returns the starting latLng for the next ring.
function breakRing(currentLat, nextLat, rings, projectedBounds, map) {
var ring = rings[rings.length - 1];
// Calculate two points for the anti-meridian crossing.
var breakLat = calculateAntimeridianLat(currentLat, nextLat);
var breakLatLngs = [new L.LatLng(breakLat, 180), new L.LatLng(breakLat, -180)];
// Add in first anti-meridian latlng to this ring to finish it.
// Positive if positive, negative if negative.
if (sign(currentLat.lng) > 0) {
pushLatLng(ring, projectedBounds, breakLatLngs.shift(), map);
} else {
pushLatLng(ring, projectedBounds, breakLatLngs.pop(), map);
}
// Return the second anti-meridian latlng
return breakLatLngs.pop();
}
/*
* @namespace L.Wrapped
* A polyline that will automatically split and wrap around the Antimeridian (Internation Date Line).
*/
var Polyline = L.Polyline.extend({
// recursively turns latlngs into a set of rings with projected coordinates
_projectLatlngs: function (latlngs, result, projectedBounds) {
var flat = latlngs[0] instanceof L.LatLng;
if (flat) {
this._createRings(latlngs, result, projectedBounds);
} else {
for (var i = 0; i < latlngs.length; i++) {
this._projectLatlngs(latlngs[i], result, projectedBounds);
}
}
},
// Creates the rings used to render the latlngs.
_createRings: function (latlngs, rings, projectedBounds) {
var len = latlngs.length;
rings.push([]);
for (var i = 0; i < len; i++) {
var compareLatLng = this._getCompareLatLng(i, len, latlngs);
pushLatLng(rings[rings.length - 1], projectedBounds, latlngs[i], this._map);
// If the next point to check exists, then check to see if the
// ring should be broken.
if (compareLatLng && isBreakRing(compareLatLng, latlngs[i])) {
var secondMeridianLatLng = breakRing(latlngs[i], compareLatLng,
rings, projectedBounds, this._map);
this._startNextRing(rings, projectedBounds, secondMeridianLatLng);
}
}
},
// returns the latlng to compare the current latlng to.
_getCompareLatLng: function (i, len, latlngs) {
return (i + 1 < len) ? latlngs[i + 1] : null;
},
// Starts a new ring and adds the second meridian point.
_startNextRing: function (rings, projectedBounds, secondMeridianLatLng) {
var ring = [];
rings.push(ring);
pushLatLng(ring, projectedBounds, secondMeridianLatLng, this._map);
}
});
// @factory L.wrappedPolyline(latlngs: LatLng[], options?: Polyline options)
// Instantiates a polyline that will automatically split around the
// antimeridian (Internation Date Line) if that is a shorter path.
function wrappedPolyline(latlngs, options) {
return new L.Wrapped.Polyline(latlngs, options);
}
/*
* @namespace L.Wrapped
* A polygon that will automatically split and wrap around the Antimeridian (Internation Date Line).
*/
var Polygon = L.Polygon.extend({
// recursively turns latlngs into a set of rings with projected coordinates
_projectLatlngs: function (latlngs, result, projectedBounds) {
var flat = latlngs[0] instanceof L.LatLng;
if (flat) {
this._createRings(latlngs, result, projectedBounds);
} else {
for (var i = 0; i < latlngs.length; i++) {
this._projectLatlngs(latlngs[i], result, projectedBounds);
}
}
},
// Creates the rings used to render the latlngs.
_createRings: function (latlngs, rings, projectedBounds) {
var len = latlngs.length;
rings.push([]);
for (var i = 0; i < len; i++) {
// Because this is a polygon, there will always be a comparison latlng
var compareLatLng = this._getCompareLatLng(i, len, latlngs);
pushLatLng(rings[rings.length - 1], projectedBounds, latlngs[i], this._map);
// Check to see if the ring should be broken.
if (isBreakRing(compareLatLng, latlngs[i])) {
var secondMeridianLatLng = breakRing(latlngs[i], compareLatLng,
rings, projectedBounds, this._map);
this._startNextRing(rings, projectedBounds, secondMeridianLatLng, i === len - 1);
}
}
// Join the last two rings if needed.
this._joinLastRing(rings, latlngs);
},
// Starts a new ring if needed and adds the second meridian point to the
// correct ring.
_startNextRing: function (rings, projectedBounds, secondMeridianLatLng, isLastLatLng) {
var ring;
if (!isLastLatLng) {
ring = [];
rings.push(ring);
pushLatLng(ring, projectedBounds, secondMeridianLatLng, this._map);
} else {
// If this is the last latlng, don't bother starting a new ring.
// instead, join the last meridian point to the first point, to connect
// the shape correctly.
ring = rings[0];
ring.unshift(this._map.latLngToLayerPoint(secondMeridianLatLng));
projectedBounds.extend(ring[0]);
}
},
// returns the latlng to compare the current latlng to.
_getCompareLatLng: function (i, len, latlngs) {
return (i + 1 < len) ? latlngs[i + 1] : latlngs[0];
},
// Joins the last ring to the first if they were accidently disconnected by
// crossing the anti-meridian
_joinLastRing: function (rings, latlngs) {
var firstRing = rings[0];
var lastRing = rings[rings.length - 1];
// If both either the first or last latlng cross the meridian immediately, then
// they have accidently been split by turning one ring into mulitiple.
// Rejoin them.
if (rings.length > 1 && (firstRing.length === 2 || lastRing.length === 2) &&
!isCrossMeridian(latlngs[0], latlngs[latlngs.length - 1])) {
var len = lastRing.length;
for (var i = 0; i < len; i++) {
firstRing.unshift(lastRing.pop());
}
// Remove the empty ring.
rings.pop();
}
}
});
// @factory L.wrappedPolygon(latlngs: LatLng[], options?: Polygon options)
// Instantiates a polygon that will automatically split around the
// antimeridian (Internation Date Line) if that is a shorter path.
function wrappedPolygon(latlngs, options) {
return new L.Wrapped.Polygon(latlngs, options);
}
exports.version = version;
exports.Polyline = Polyline;
exports.wrappedPolyline = wrappedPolyline;
exports.Polygon = Polygon;
exports.wrappedPolygon = wrappedPolygon;
exports.calculateAntimeridianLat = calculateAntimeridianLat;
exports.isCrossMeridian = isCrossMeridian;
exports.sign = sign;
exports.isBreakRing = isBreakRing;
})));
//# sourceMappingURL=leaflet.antimeridian-src.js.map