content: package org.oscim.layers.vector;
  
  import org.oscim.core.Box;
  import org.oscim.core.GeometryBuffer;
  import org.oscim.core.MapPosition;
  import org.oscim.event.Event;
  import org.oscim.layers.Layer;
  import org.oscim.map.Map;
  import org.oscim.map.Map.UpdateListener;
  import org.oscim.map.Viewport;
  import org.oscim.renderer.BucketRenderer;
  import org.oscim.renderer.GLViewport;
  import org.oscim.renderer.bucket.RenderBuckets;
  import org.oscim.utils.async.SimpleWorker;
  import org.oscim.utils.geom.TileClipper;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  public abstract class AbstractVectorLayer<T> extends Layer implements UpdateListener {
      public static final Logger log = LoggerFactory.getLogger(AbstractVectorLayer.class);
  
      protected static final double UNSCALE_COORD = 4;
  
      protected final GeometryBuffer mGeom = new GeometryBuffer(128, 4);
      protected final TileClipper mClipper = new TileClipper(-1024, -1024, 1024, 1024);
  
      protected final Worker mWorker;
      protected long mUpdateDelay = 50;
  
      protected boolean mUpdate = true;
  
      public AbstractVectorLayer(Map map) {
          super(map);
          mWorker = new Worker(mMap);
          mRenderer = new Renderer();
      }
  
      @Override
      public void onDetach() {
          super.onDetach();
  
          mWorker.cancel(true);
      }
  
      @Override
      public void onMapEvent(Event e, MapPosition pos) {
          if (mUpdate) {
              mUpdate = false;
              mWorker.submit(0);
          } else if (e == Map.POSITION_EVENT || e == Map.CLEAR_EVENT) {
              /* throttle worker */
              mWorker.submit(mUpdateDelay);
          }
      }
  
      public void update() {
          mWorker.submit(0);
      }
  
      protected abstract void processFeatures(Task t, Box b);
  
      protected static class Task {
          public final RenderBuckets buckets = new RenderBuckets();
          public final MapPosition position = new MapPosition();
      }
  
      protected class Worker extends SimpleWorker<Task> {
  
          public Worker(Map map) {
              super(map, 50, new Task(), new Task());
          }
  
          /**
           * automatically in sync with worker thread
           */
          @Override
          public void cleanup(Task t) {
              if (t.buckets != null)
                  t.buckets.clear();
          }
  
          /**
           * running on worker thread
           */
          @Override
          public boolean doWork(Task t) {
  
              Box bbox;
              float[] box = new float[8];
  
              Viewport v = mMap.viewport().getSyncViewport();
              synchronized (v) {
                  bbox = v.getBBox(null, 0);
                  v.getMapExtents(box, 0);
                  v.getMapPosition(t.position);
              }
  
              /* Hmm what is this for? */
              //    double scale = t.position.scale * Tile.SIZE;
              //    t.position.x = (long) (t.position.x * scale) / scale;
              //    t.position.y = (long) (t.position.y * scale) / scale;
  
              bbox.map2mercator();
  
              //    double xmin = bbox.xmin;
              //    double xmax = bbox.xmax;
              //    Box lbox = null;
              //    Box rbox = null;
              //    if (bbox.xmin < -180) {
              //        bbox.xmin = -180;
              //        lbox = new Box(bbox);
              //    }
              //    if (bbox.xmax > 180) {
              //        bbox.xmax = 180;
              //        rbox = new Box(bbox);
              //    }
  
              processFeatures(t, bbox);
  
              //if (lbox != null) {
              //    t.position.x += 1;
              //    lbox.xmax = 180;
              //    lbox.xmin = xmin + 180;
              //    processFeatures(t, lbox);
              //    t.position.x -= 1;
              //}
              //
              //if (rbox != null) {
              //    t.position.x -= 1;
              //    rbox.xmin = -180;
              //    rbox.xmax = xmax - 180;
              //    processFeatures(t, rbox);
              //    t.position.x += 1;
              //}
  
              t.buckets.prepare();
  
              mMap.render();
              return true;
          }
      }
  
      public class Renderer extends BucketRenderer {
          MapPosition mTmpPos = new MapPosition();
  
          public Renderer() {
              mFlipOnDateLine = true;
          }
  
          @Override
          public void update(GLViewport v) {
  
              Task t = mWorker.poll();
  
              if (t == null)
                  return;
  
              mMapPosition.copy(t.position);
              mMapPosition.setScale(mMapPosition.scale / UNSCALE_COORD);
  
              buckets.setFrom(t.buckets);
  
              compile();
          }
      }
  }
  