Search code examples
javascriptmappingopenlayersintersectiongeojson

Tools or libraries for getting cross-section of 2D polygons into 1D geometry?


I am an amateur pilot working on some airspace modeling experiments. What I am trying to achieve is a tool for easily creating cross-sections of airspace, i.e. 3D airspace to 2D. So in the end I would like to have an image similar to this created: airspace example. Accuracy is not important as these cross-sections would not be used for navigational purposes, but for training/visualization only. Hence coordinate geometry is enough, and no geodesic calculations are needed.

Currently I am storing GeoJSON 2D geometries (all polygons) in my database with additional metadata containing lower and upper altitude limits of each airspace element. Therefore I am effectively only showing 2D data on my OpenLayers and Leaflet.js maps.

I want the user to be able to draw a linestring over the map (see the green linestring in the picture below, stroke-width dramatically increased for demonstration purposes). This I can do with OpenLayers or Leaflet. The outcome should be a 1-dimensional cross-section of the 2D elements intersecting with the user-drawn line, as in this very artistic illustration by me:

Cross section 2D -> 1D

Clarification: if the length of the output 1-dimensional cross-section is for example 1L, then the contents of the cross-section in the example should be a set of the following geometries: 1) black line between 0.1L and 0.5L and 2) a red line between 0.7L and 0.825L.

The user interface part is doable, and it would be running on top of OpenLayers or Leaflet. I have also found several algorithms in various languages for determining if two lines intersect and even to find out the intersection point. I can use Raphael.js then to draw the cross-section.

I should be able to do all this in a day or two... But I was wondering if there was an easier path to take? For example, does anyone know of a software library that would enable calculation of such cross-sections that I am trying to achieve? Oh, please don't mention those $10,000 GIS packages :-).

As this will be a web application, I am mostly looking into Javascript, Perl or PHP solutions. GeoJSON Utilities for JavaScript looks quite promising for calculating intersections, but I wonder if there are others?


Solution

  • Posting answer to my own question for future reference. I was able to come up with a solution for creating the airspace cross-sections. It is based on coordinate geometry.

    1) Input data is the start and end coordinates of the cross-section.

    2) Loop through all areas and check if the start and/or end coordinates fall inside any of the airspace areas (polygons). PHP code slightly modified from another SO answer to check if a point is inside a polygon:

    // Point class, storage of lat/long-pairs
    class Point {
      var $lat;
      var $long;
      function Point($lat, $long) {
        $this->lat = $lat;
        $this->long = $long;
      }
    }
    
    function pointInPolygon($p, $polygon) {
      $c = 0;
      $p1 = $polygon[0];
      $n = count($polygon);
    
      for ($i=1; $i<=$n; $i++) {
        $p2 = $polygon[$i % $n];
        if ($p->long > min($p1->long, $p2->long)
            && $p->long <= max($p1->long, $p2->long)
            && $p->lat <= max($p1->lat, $p2->lat)
            && $p1->long != $p2->long) {
                $xinters = ($p->long - $p1->long) * ($p2->lat - $p1->lat) / ($p2->long - $p1->long)
           if ($p1->lat == $p2->lat || $p->lat <= $xinters) {
             $c++;
           }
        }
        $p1 = $p2;
      }
      // if the number of edges we passed through is even, then it's not in the poly.
      return $c%2!=0;
    }
    

    3) Loop through all the line segments of each and every area (polygon) containing the airspace data. PHP code slightly modified from another SO answer to return the intersection point ofe two line segments:

    function Det2($x1, $x2, $y1, $y2) {
      return ($x1 * $y2 - $y1 * $x2);
    }
    
    function lineIntersection ($v1Y, $v1X, $v2Y, $v2X, $v3Y, $v3X, $v4Y, $v4X) {
      $tolerance = 0.000001;
    
      $a = Det2($v1X - $v2X, $v1Y - $v2Y, $v3X - $v4X, $v3Y - $v4Y);
      if (abs($a) < $tolerance) return null; // Lines are parallel
    
      $d1 = Det2($v1X, $v1Y, $v2X, $v2Y);
      $d2 = Det2($v3X, $v3Y, $v4X, $v4Y);
      $x = Det2($d1, $v1X - $v2X, $d2, $v3X - $v4X) / $a;
      $y = Det2($d1, $v1Y - $v2Y, $d2, $v3Y - $v4Y) / $a;
    
      if ($x < min($v1X, $v2X) - $tolerance || $x > max($v1X, $v2X) + $tolerance) return null;
      if ($y < min($v1Y, $v2Y) - $tolerance || $y > max($v1Y, $v2Y) + $tolerance) return null;
      if ($x < min($v3X, $v4X) - $tolerance || $x > max($v3X, $v4X) + $tolerance) return null;
      if ($y < min($v3Y, $v4Y) - $tolerance || $y > max($v3Y, $v4Y) + $tolerance) return null;
    
      return array($x, $y);
    }
    

    4) If there are intersecting segments, figure out the distance of the intersection (as result of the previous step) from the start coordinates of the cross-section. Divide this with the overall length of the cross-section to get the relative location of the airspace area boundary on the cross-section line.

    5) Based on results of points 2 and 4, draw SVG polygons. Relative intersection locations are translated to X coordinates of polygons and altitude data (lower and upper limit) becomes the Y coordinates of the polygon.