text: import { SphMercProjection } from "./sphmerc-projection.js";
  
  class LocationBased {
    constructor(scene, camera, options = {}) {
      this._scene = scene;
      this._camera = camera;
      this._proj = new SphMercProjection();
      this._eventHandlers = {};
      this._lastCoords = null;
      this._gpsMinDistance = 0;
      this._gpsMinAccuracy = 1000;
      this._watchPositionId = null;
      this.setGpsOptions(options);
    }
  
    setProjection(proj) {
      this._proj = proj;
    }
  
    setGpsOptions(options = {}) {
      if (options.gpsMinDistance !== undefined) {
        this._gpsMinDistance = options.gpsMinDistance;
      }
      if (options.gpsMinAccuracy !== undefined) {
        this._gpsMinAccuracy = options.gpsMinAccuracy;
      }
    }
  
    startGps(maximumAge = 0) {
      if (this._watchPositionId === null) {
        this._watchPositionId = navigator.geolocation.watchPosition(
          (position) => {
            this._gpsReceived(position);
          },
          (error) => {
            if (this._eventHandlers["gpserror"]) {
              this._eventHandlers["gpserror"](error.code);
            } else {
              alert(`GPS error: code ${error.code}`);
            }
          },
          {
            enableHighAccuracy: true,
            maximumAge: maximumAge,
          }
        );
        return true;
      }
      return false;
    }
  
    stopGps() {
      if (this._watchPositionId !== null) {
        navigator.geolocation.clearWatch(this._watchPositionId);
        this._watchPositionId = null;
        return true;
      }
      return false;
    }
  
    fakeGps(lon, lat, elev = null, acc = 0) {
      if (elev !== null) {
        this.setElevation(elev);
      }
      this._gpsReceived({
        coords: {
          longitude: lon,
          latitude: lat,
          accuracy: acc,
        },
      });
    }
  
    lonLatToWorldCoords(lon, lat) {
      const projectedPos = this._proj.project(lon, lat);
      return [projectedPos[0], -projectedPos[1]];
    }
  
    add(object, lon, lat, elev) {
      this.setWorldPosition(object, lon, lat, elev);
      this._scene.add(object);
    }
  
    setWorldPosition(object, lon, lat, elev) {
      const worldCoords = this.lonLatToWorldCoords(lon, lat);
      [object.position.x, object.position.z] = worldCoords;
      if (elev !== undefined) {
        object.position.y = elev;
      }
    }
  
    setElevation(elev) {
      this._camera.position.y = elev;
    }
  
    on(eventName, eventHandler) {
      this._eventHandlers[eventName] = eventHandler;
    }
  
    _gpsReceived(position) {
      let distMoved = Number.MAX_VALUE;
      if (position.coords.accuracy <= this._gpsMinAccuracy) {
        if (this._lastCoords === null) {
          this._lastCoords = {
            latitude: position.coords.latitude,
            longitude: position.coords.longitude,
          };
        } else {
          distMoved = this._haversineDist(this._lastCoords, position.coords);
        }
        if (distMoved >= this._gpsMinDistance) {
          this._lastCoords.longitude = position.coords.longitude;
          this._lastCoords.latitude = position.coords.latitude;
  
          this.setWorldPosition(
            this._camera,
            position.coords.longitude,
            position.coords.latitude
          );
          if (this._eventHandlers["gpsupdate"]) {
            this._eventHandlers["gpsupdate"](position, distMoved);
          }
        }
      }
    }
  
    /**
     * Calculate haversine distance between two lat/lon pairs.
     *
     * Taken from original A-Frame components
     */
    _haversineDist(src, dest) {
      const dlongitude = THREE.Math.degToRad(dest.longitude - src.longitude);
      const dlatitude = THREE.Math.degToRad(dest.latitude - src.latitude);
  
      const a =
        Math.sin(dlatitude / 2) * Math.sin(dlatitude / 2) +
        Math.cos(THREE.Math.degToRad(src.latitude)) *
          Math.cos(THREE.Math.degToRad(dest.latitude)) *
          (Math.sin(dlongitude / 2) * Math.sin(dlongitude / 2));
      const angle = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
      return angle * 6371000;
    }
  }
  
  export { LocationBased };
  