'use strict';

var linematch = require('linematch');
var lineclip = require('lineclip');

module.exports = function(data, tile, done) {

  // filter and normalize input geometry
  var tiger = toLines(data.tiger.tiger2015);
  var streets = toLines(data.osm.osm);

  // find tiger parts that are not covered by streets within 10 pixels
  var diff = linematch(tiger, streets, 10);

  if (diff.length) {
    // write a feature with the diff as MultiLineString
    process.stdout.write(',' + JSON.stringify({
      type: 'Feature',
      properties: {},
      geometry: {
        type: 'MultiLineString',
        coordinates: toGeoJSON(diff, tile)
      }
    }));
  }

  done(null, null);
};

function toGeoJSON(diff, tile) {
  var size = 4096 * Math.pow(2, tile[2]);
  var x0 = 4096 * tile[0];
  var y0 = 4096 * tile[1];

  for (var i = 0; i < diff.length; i++) {
    for (var j = 0; j < diff[i].length; j++) {
      var p = diff[i][j];
      diff[i][j] = [
        round((p[0] + x0) * 360 / size - 180),
        round(360 / Math.PI * Math.atan(Math.exp((180 - (p[1] + y0) * 360 / size) * Math.PI / 180)) - 90)
      ];
    }
  }
  return diff;
}

function round(num) {
  return Math.round(num * 1e6) / 1e6;
}

function toLines(layer) {
  var lines = [];
  var bbox = [0, 0, 4096, 4096];

  for (var i = 0; i < layer.length; i++) {
    var feature = layer.feature(i);

    // only consider polygon features with Tiger name or OSM highway tag
    if (feature.type === 2 && (feature.properties.FULLNAME !== '' || feature.properties.highway)) {
      var geom = feature.loadGeometry();

      // normalize line geometry
      var line = [];
      for (var k = 0; k < geom.length; k++) {
        for (var j = 0; j < geom[k].length; j++) {
          line.push([
            geom[k][j].x * 4096 / layer.extent,
            geom[k][j].y * 4096 / layer.extent
          ]);
        }
      }

      // only consider lines that are longer than 20 pixels
      if (dist(line) >= 20) {
        lineclip(line, bbox, lines); // clip to tile bbox
      }
    }
  }
  return lines;
}

function dist(line) { // approximate distance
  var d = 0;
  for (var i = 1; i < line.length; i++) {
    var dx = line[i][0] - line[i - 1][0];
    var dy = line[i][1] - line[i - 1][1];
    d += Math.sqrt(dx * dx + dy * dy);
  }
  return d;
}

@@ -326,16 +326,59 @@
 0 pixels
+;%0A  // filter out chunks that are too short
 %0A  var d
@@ -412,16 +412,36 @@
 ets, 10)
+.filter(filterShort)
 ;%0A%0A  if 
@@ -990,16 +990,63 @@
 %5Bi%5D%5Bj%5D;%0A
+      var y2 = 180 - (p%5B1%5D + y0) * 360 / size;%0A
       di
@@ -1158,40 +1158,10 @@
 exp(
-(180 - (p%5B1%5D + y0) * 360 / size)
+y2
  * M
@@ -1180,23 +1180,16 @@
 )) - 90)
-%0A      
 %5D;%0A    %7D
@@ -1653,19 +1653,73 @@
 %0A%0A      
-// 
+for (var k = 0; k %3C geom.length; k++) %7B%0A        lineclip(
 normaliz
@@ -1723,96 +1723,186 @@
 lize
- line 
+Line(
 geom
-etry%0A      var line = %5B%5D;%0A      for (var k = 0; k %3C geom.length; k++) %7B%0A      
+%5Bk%5D, layer.extent), bbox, lines); // clip to tile bbox and add to result%0A      %7D%0A    %7D%0A  %7D%0A  return lines;%0A%7D%0A%0Afunction normalizeLine(line, extent) %7B%0A  var newLine = %5B%5D;%0A
   fo
@@ -1912,26 +1912,23 @@
 var 
-j
+i
  = 0; 
-j %3C geom%5Bk%5D
+i %3C line
 .len
@@ -1924,33 +1924,33 @@
  %3C line.length; 
-j
+i
 ++) %7B%0A          
@@ -1943,23 +1943,20 @@
 ) %7B%0A    
-      l
+newL
 ine.push
@@ -1964,31 +1964,22 @@
 %5B%0A      
-      geom%5Bk%5D%5Bj
+line%5Bi
 %5D.x * 40
@@ -1979,30 +1979,24 @@
 .x * 4096 / 
-layer.
 extent,%0A    
@@ -2001,263 +2001,154 @@
     
-      geom%5Bk%5D%5Bj%5D.y * 4096 / layer.extent%0A          %5D);%0A        %7D%0A      %7D%0A%0A      // only consider lines that are longer than 20 pixels%0A      if (dist(line) %3E= 20) %7B%0A        lineclip(line, bbox, lines); // clip to tile bbox%0A      %7D%0A    %7D%0A  %7D%0A  return lines;
+line%5Bi%5D.y * 4096 / extent%5D);%0A  %7D%0A  return newLine;%0A%7D%0A%0Afunction filterShort(line) %7B%0A  return dist(line) %3E= 30; // line length is at least 30 pixels
 %0A%7D%0A%0A

improve the diff example