AndrewLinton

Cubic Spline between Placemarks in Google Earth

Jan 1st, 2014
430
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  2.     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  3. <html xmlns="http://www.w3.org/1999/xhtml">
  4. <head>
  5.     <title>Cubic Spline Experiments</title>  
  6.     <script type="text/javascript" src="script/extensions-0.2.1.js"></script>
  7.     <script type="text/javascript" src="https://www.google.com/jsapi"></script>
  8.     <script type="text/javascript" language="javascript">
  9.         var ge;                         // Google Earth instance
  10.         var w, d, e, g;                 // variables for window, document, documentElement, and body
  11.         var dragInfo = null;            // variable identifying the Placemark being dragged
  12.         var designInput = null;         // kmlObject holding the Placemarks
  13.         var gex;                        // Google Earth Extensions instance
  14.         var placeMarks = [];            // array of Placemarks extracted from designInput
  15.         var lineStringIndex = 0;        // LineString id suffix to ensure uniqueness
  16.  
  17.         google.load("earth", "1");      // load Google Earth
  18.         google.setOnLoadCallback(init); // identify callback method
  19.  
  20.         function init() {
  21.             w = window;                 // get abbreviated names for window, document etc.              
  22.             d = document;               // for use in the resize method
  23.             e = d.documentElement;
  24.             g = d.getElementsByTagName('body')[0];
  25.             w.onresize = resize;
  26.  
  27.             // create the Google Earth instance, specifying callback methods
  28.             google.earth.createInstance('viewKML', kmlViewerCreated, failureCB);
  29.  
  30.             // add buttons to fetch a KMLfile and calculate the spline curve
  31.             addButton('Open File', fetchInput);
  32.             addButton('Fit Spline Curve', calculateSpline);
  33.             resize();                   // resize the Google Earth instance
  34.  
  35.         }
  36.  
  37.         // resize the Google Earth instance to fill the available screen
  38.         function resize() {
  39.             var x = w.innerWidth || e.clientWidth || g.clientWidth;
  40.             var y = w.innerHeight || e.clientHeight || g.clientHeight;
  41.             var v = document.getElementById('viewKML');
  42.             var counter = 0;
  43.             if (v) {
  44.                 v.style.width = (x - 20) + 'px';
  45.                 v.style.height = (y - 80) + 'px';
  46.             }
  47.         }
  48.  
  49.         // handle the successful creation of the Google Earth instance
  50.         function kmlViewerCreated(instance) {
  51.             ge = instance;                      // short name for the Google Earth instance
  52.             gex = new GEarthExtensions(ge);     // create instance of the GE Extensions API
  53.  
  54.             // SET UP EVENT LISTENERS TO IMPLEMENT PLACEMARK DRAGGING
  55.             // ======================================================
  56.             // This code was copied from the Code Playground at:
  57.             // http://code.google.com/apis/ajax/playground/?exp=earth#draggable_placemark
  58.             // listen for mousedown on the window (look specifically for point placemarks)
  59.             google.earth.addEventListener(ge.getWindow(), 'mousedown', function (event) {
  60.                 if (event.getTarget().getType() == 'KmlPlacemark' &&
  61.                     event.getTarget().getGeometry().getType() == 'KmlPoint') {
  62.                     var placemark = event.getTarget();
  63.                     dragInfo = {
  64.                         placemark: event.getTarget(),
  65.                         dragged: false
  66.                     };
  67.                 }
  68.             });
  69.  
  70.             // listen for mousemove on the globe
  71.             google.earth.addEventListener(ge.getGlobe(), 'mousemove', function (event) {
  72.                 if (dragInfo) {
  73.                     event.preventDefault();
  74.                     var point = dragInfo.placemark.getGeometry();
  75.                     point.setLatitude(event.getLatitude());
  76.                     point.setLongitude(event.getLongitude());
  77.                     dragInfo.dragged = true;
  78.                 }
  79.             });
  80.  
  81.             // listen for mouseup on the window
  82.             google.earth.addEventListener(ge.getWindow(), 'mouseup', function (event) {
  83.                 if (dragInfo) {
  84.                     if (dragInfo.dragged) {
  85.                         // if the placemark was dragged, prevent balloons from popping up
  86.                         event.preventDefault();
  87.                     }
  88.                     dragInfo = null;
  89.                     clearLinestrings(); // when a drag completes, remove the previous LineString
  90.                     calculateSpline();  // and recalculate the spline using the new points
  91.                 }
  92.             });
  93.             // END OF DRAGGABLE PLACEMARK CODE
  94.         }
  95.         function failureCB(errorCode) {
  96.             alert('Failed to create Google Earth instance with error: ' + errorCode);
  97.         }
  98.  
  99.         // get a kml file which holds a set of Placemarks
  100.         function fetchInput() {
  101.  
  102.             // identify the kml file to load
  103.             // =============================
  104.             // set the URL of your own file here.
  105.             var href = '*************************';
  106.  
  107.             // get the kml file and show its placemarks
  108.             google.earth.fetchKml(ge, href, function (kmlObject) {
  109.                 if (kmlObject) {
  110.                     designInput = kmlObject;
  111.                     ge.getFeatures().appendChild(kmlObject);
  112.  
  113.                     // if there's a view available, use it
  114.                     if (kmlObject.getAbstractView())
  115.                         ge.getView().setAbstractView(kmlObject.getAbstractView());
  116.                 }
  117.             });
  118.         }
  119.         // calculate and display a cubic spline curve through the given Placemarks
  120.         function calculateSpline() {
  121.  
  122.             // extract the Placemarks as an array
  123.             placeMarks = getPlacemarks(designInput);
  124.  
  125.             // if Placemaraks were found
  126.             if (placeMarks.length > 0) {
  127.                 var i = 0;      // while loop index
  128.                 var j;          // interpolation index
  129.                 var x = [];     // array of longitudes
  130.                 var y = [];     // array of latitudes
  131.                 var d = [];     // array of derivatives
  132.  
  133.                 // for each Placemark in the array
  134.                 while (i < placeMarks.length) {
  135.                     var point = placeMarks[i++].getGeometry();  // get the geometry
  136.                     x.push(point.getLongitude());               // extract longitude
  137.                     y.push(point.getLatitude());                // and latitude
  138.                 }
  139.  
  140.                 // calculate array of derivatives
  141.                 CubicSpline.derivatives(x, y, d);
  142.  
  143.                 // create a Placemark with a LineString geometry, giving it a unique id
  144.                 var lineStringPlacemark = ge.createPlacemark('SplineCurve' + lineStringIndex++);
  145.                 var lineString = ge.createLineString('');
  146.                 lineStringPlacemark.setGeometry(lineString);
  147.  
  148.                 i = 0;
  149.                 // for each pair of points
  150.                 while (i < placeMarks.length) {
  151.  
  152.                     // divide the difference in longitude into 100 increments
  153.                     var deltaLong = (x[i + 1] - x[i]) / 100;
  154.  
  155.                     // for each intermediate point
  156.                     for (j = 0; j < 100; j++) {
  157.  
  158.                         // calculate its longitude
  159.                         var lng = x[i] + (j * deltaLong);
  160.  
  161.                         // interpolate the latitude
  162.                         var lat = CubicSpline.interpolate(lng, x, y, d);
  163.  
  164.                         // if a valid point is derived
  165.                         if (!(isNaN(lat) || isNaN(lng))) {
  166.  
  167.                             // add the point to the LineString geometry
  168.                             addToLineString(lineString, lat, lng, 0, 0, 0,1.0);
  169.                         }
  170.                     }
  171.                     i++;
  172.                 }
  173.                 // add the Placemark to Google Earth
  174.                 ge.getFeatures().appendChild(lineStringPlacemark);
  175.             }
  176.             else {
  177.                 alert('no placemarks found');
  178.             }
  179.         }
  180.         // extract Placemarks to an array
  181.         function getPlacemarks(input) {
  182.  
  183.             var result = [];    // array of Placemarks returned to caller
  184.  
  185.             // the required objects exist
  186.             if (input && gex) {
  187.  
  188.                 // get the Placemark Folder (in the sample file they are
  189.                 // in a Folder with id set to 'PlacemarkFolder')
  190.                 var pf = getObjectById('PlacemarkFolder');
  191.  
  192.                 // if the Placemark Folder is found
  193.                 if (pf) {
  194.  
  195.                     // Walk fhe Folder finding the Placemarks
  196.                     gex.dom.walk({
  197.                         rootObject: pf,
  198.                         visitCallback: function () {
  199.                             if ('getType' in this && this.getType() == 'KmlPlacemark')
  200.                                 result.push(this);
  201.                         }
  202.                     });
  203.                 }
  204.             }
  205.             // return a Placemark array to the caller
  206.             return result;
  207.         }
  208.  
  209.         // add a segment to a LineString
  210.         function addToLineString(lineString, lat, lng, latOffset, lngOffset, altitude) {
  211.             lineString.getCoordinates().
  212.               pushLatLngAlt(lat + latOffset, lng + lngOffset, altitude);
  213.         }
  214.  
  215.         // clear all Placemarks identified as SplineCurves
  216.         function clearLinestrings() {
  217.  
  218.             gex.dom.walk({
  219.                 rootObject: ge,
  220.                 visitCallback: function () {
  221.  
  222.                     // if the current node is a Placemark whose id begins with 'SplineCurve'
  223.                     if ('getType' in this && this.getType() == 'KmlPlacemark') {
  224.                         if (this.getId().indexOf('SplineCurve') >= 0) {
  225.  
  226.                             // remove from the Feature Container
  227.                             ge.getFeatures().removeChild(this);
  228.                         }
  229.                     }
  230.                 }
  231.             });
  232.         }
  233.  
  234.         // find an object in the Google Earth instance with the given id
  235.         function getObjectById(id) {
  236.             var foundObject = null;
  237.  
  238.             // Traverse the DOM until the object is found
  239.             gex.dom.walk({
  240.                 rootObject: ge,
  241.                 visitCallback: function () {
  242.                     if ('getId' in this && this.getId() == id) {
  243.                         foundObject = this;
  244.                         return false;  // Stop the walk.
  245.                     }
  246.                 }
  247.             });
  248.             return foundObject;
  249.         }
  250.  
  251.         // flexible way to add command buttons
  252.         // (as used widely in the Code Playground)
  253.         function addButton(caption, clickHandler) {
  254.             var btn = document.createElement('input');
  255.             btn.type = 'button';
  256.             btn.value = caption;
  257.  
  258.             if (btn.attachEvent)
  259.                 btn.attachEvent('onclick', clickHandler);
  260.             else
  261.                 btn.addEventListener('click', clickHandler, false);
  262.  
  263.             // add the button to the Sample UI
  264.             document.getElementById('content').appendChild(btn);
  265.         }
  266.         function CubicSpline() { };
  267.  
  268.         CubicSpline._gaussJ = {};
  269.         CubicSpline._gaussJ.solve = function (A, x) // in Matrix, out solutions
  270.         {
  271.             var m = A.length;
  272.             for (var k = 0; k < m; k++) // column
  273.             {
  274.                 // pivot for column
  275.                 var i_max = 0; var vali = Number.NEGATIVE_INFINITY;
  276.                 for (var i = k; i < m; i++) if (A[i][k] > vali) { i_max = i; vali = A[i][k]; }
  277.                 CubicSpline._gaussJ.swapRows(A, k, i_max);
  278.  
  279.                 if (A[i_max][i] == 0) console.log("matrix is singular!");
  280.  
  281.                 // for all rows below pivot
  282.                 for (var i = k + 1; i < m; i++) {
  283.                     for (var j = k + 1; j < m + 1; j++)
  284.                         A[i][j] = A[i][j] - A[k][j] * (A[i][k] / A[k][k]);
  285.                     A[i][k] = 0;
  286.                 }
  287.             }
  288.  
  289.             for (var i = m - 1; i >= 0; i--)    // rows = columns
  290.             {
  291.                 var v = A[i][m] / A[i][i];
  292.                 x[i] = v;
  293.                 for (var j = i - 1; j >= 0; j--)    // rows
  294.                 {
  295.                     A[j][m] -= A[j][i] * v;
  296.                     A[j][i] = 0;
  297.                 }
  298.             }
  299.         }
  300.         CubicSpline._gaussJ.zerosMat = function (r, c) { var A = []; for (var i = 0; i < r; i++) { A.push([]); for (var j = 0; j < c; j++) A[i].push(0); } return A; }
  301.         CubicSpline._gaussJ.printMat = function (A) { for (var i = 0; i < A.length; i++) console.log(A[i]); }
  302.         CubicSpline._gaussJ.swapRows = function (m, k, l) { var p = m[k]; m[k] = m[l]; m[l] = p; }
  303.  
  304.  
  305.         CubicSpline.derivatives = function (xs, ys, ks) // in x values, in y values, out k values
  306.         {
  307.             var n = xs.length - 1;
  308.             var A = CubicSpline._gaussJ.zerosMat(n + 1, n + 2);
  309.  
  310.             for (var i = 1; i < n; i++) // rows
  311.             {
  312.                 A[i][i - 1] = 1 / (xs[i] - xs[i - 1]);
  313.  
  314.                 A[i][i] = 2 * (1 / (xs[i] - xs[i - 1]) + 1 / (xs[i + 1] - xs[i]));
  315.  
  316.                 A[i][i + 1] = 1 / (xs[i + 1] - xs[i]);
  317.  
  318.                 A[i][n + 1] = 3 * ((ys[i] - ys[i - 1]) / ((xs[i] - xs[i - 1]) * (xs[i] - xs[i - 1])) + (ys[i + 1] - ys[i]) / ((xs[i + 1] - xs[i]) * (xs[i + 1] - xs[i])));
  319.             }
  320.  
  321.             A[0][0] = 2 / (xs[1] - xs[0]);
  322.             A[0][1] = 1 / (xs[1] - xs[0]);
  323.             A[0][n + 1] = 3 * (ys[1] - ys[0]) / ((xs[1] - xs[0]) * (xs[1] - xs[0]));
  324.  
  325.             A[n][n - 1] = 1 / (xs[n] - xs[n - 1]);
  326.             A[n][n] = 2 / (xs[n] - xs[n - 1]);
  327.             A[n][n + 1] = 3 * (ys[n] - ys[n - 1]) / ((xs[n] - xs[n - 1]) * (xs[n] - xs[n - 1]));
  328.  
  329.             CubicSpline._gaussJ.solve(A, ks);
  330.         }
  331.  
  332.         CubicSpline.interpolate = function (x, xs, ys, ks) {
  333.             var i = 1;
  334.             while (xs[i] < x) i++;
  335.  
  336.             var t = (x - xs[i - 1]) / (xs[i] - xs[i - 1]);
  337.  
  338.             var a = ks[i - 1] * (xs[i] - xs[i - 1]) - (ys[i] - ys[i - 1]);
  339.             var b = -ks[i] * (xs[i] - xs[i - 1]) + (ys[i] - ys[i - 1]);
  340.  
  341.             var q = (1 - t) * ys[i - 1] + t * ys[i] + t * (1 - t) * (a * (1 - t) + b * t);
  342.             return q;
  343.         }
  344.  
  345.  
  346.  
  347.     </script>
  348. </head>
  349. <body>  
  350.     <div id="content">
  351.         <div id="viewKML"/>
  352.     </div>
  353. </body>
  354. </html>
Add Comment
Please, Sign In to add comment