<commit_before># FIXME - fetch kml from here http://localhost:8080/api/v1/mission/10/messages.kml
class MapController
  @$inject: ['$scope', '$http']
  constructor: (@scope, @http) ->
    @initMap()

  initMap: =>
    # Collect various maps worth using
    mbox = (key) ->
      url: "https://a.tiles.mapbox.com/v3/" + key + "/{z}/{x}/{y}.png"
      options:
        attribution: '<a href="http://www.mapbox.com/about/maps/" target="_blank">Terms &amp; Feedback</a>'

    maps =
      openstreetmap:
        url: "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        options:
          attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
      opencyclemap:
        url: "http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png",
        options:
          attribution: 'All maps &copy; <a href="http://www.opencyclemap.org">OpenCycleMap</a>, map data &copy; <a href="http://www.openstreetmap.org">OpenStreetMap</a> (<a href="http://www.openstreetmap.org/copyright">ODbL</a>'
      mapbox_bright: mbox("mapbox.world-bright")
      mapbox_example: mbox("examples.map-zr0njcqy")
      threedr_default: mbox("kevin3dr.hokdl9ko")

    @scope.defaults =
      scrollWheelZoom: true
      zoom: 10
      minZoom: 2
    @scope.tiles = maps.threedr_default

class BoundsFactory
  constructor: () ->
    @bounds =
      northEast:
        lat: -Number.MAX_VALUE
        lng: -Number.MAX_VALUE
      southWest:
        lat: Number.MAX_VALUE
        lng: Number.MAX_VALUE

  # expand bounding box to include pt
  # returns true if we changed the bounding box
  expand: (lat, lon) ->
    # Updating bounds are _very_ expensive in leaflet, so we try to 'bucket' our bounds in .1 deg increments
    roundUp = (x) -> Math.ceil(x * 10) / 10.0
    roundDown = (x) -> Math.floor(x * 10) / 10.0

    dirty = false

    # We use the dirty flag to avoid changing scope object if nothing changed
    min = (newval, old) ->
      if newval < old
        dirty = true
        newval
      else
        old

    max = (newval, old) ->
      if newval > old
        dirty = true
        newval
      else
        old

    @bounds.southWest.lat = min(roundDown(lat),@bounds.southWest.lat)
    @bounds.southWest.lng = min(roundDown(lon), @bounds.southWest.lng)
    @bounds.northEast.lat = max(roundUp(lat), @bounds.northEast.lat)
    @bounds.northEast.lng = max(roundUp(lon), @bounds.northEast.lng)
    dirty

class LiveMapController extends MapController
  @$inject: ['$scope', '$log', 'leafletData', '$http', 'missionService', 'authService']
  constructor: (scope, @log, leafletData, http, @missionService, @authService) ->
    scope.leafletData = leafletData
    @boundsFactory = new BoundsFactory

    @count = 0
    scope.vehicleMarkers = {}
    scope.vehiclePaths = {}
    scope.bounds = {} # Angular wants to see at least an empty object (not undefined)
    scope.center = {}

    # TODO: fake markers arn't workng
    #scope.vehicleMarkers['missionId_fake'] = {lat: scope.hawaii.lat, lng: scope.hawwai.lng, focus: false, draggable: false}

    super(scope, http)

    s = @missionService

    listeners =
      "loc": @onLive
      "att": @onAttitude
      "start": @onMissionUpdate
      "end": @onMissionEnd
      "mode": @updateVehicleMessage
      "arm": @updateVehicleMessage
      "update": @onMissionUpdate
      "user": @onMissionUpdate
      "mystery": @updateVehicleMessage
      "text": @updateVehicleMessage

    listenerIds = s.atmosphere.on(name, callback) for name, callback of listeners
    @log.debug('live map now subscribed')

    # We bounce our atmosphere link, because the server will automatically resend vehicle messages on each new connection
    # (And our controller is not keeping a list of past connection messages - FIXME, it would be better to keep
    # a model object to preserve such state)
    s.atmosphere_connect()

    scope.$on("$destroy", () =>
      @log.debug('Unsubscribe for atmosphere notification')
      for id of listenerIds
        s.atmosphere.off(id)
      s.atmosphere_disconnect()
    )

  onLive: (data) =>
    # Apply should be called around the function to be guarded... -kevinh
    @scope.$apply(() =>
      key = @vehicleKey(data.missionId)
      @updateVehicle(key, data.payload.lat, data.payload.lon)

      # Grow (or create) our bounds - We don't really use this feature much any more because starting with the whole world looks better
      #if @boundsFactory.expand(data.payload.lat, data.payload.lon)
      #  @scope.bounds = @boundsFactory.bounds
    )

  onAttitude: (data) =>
    # FIXME - not sure if I need apply... (or how to optimize it)
    # Apply should be called around the function to be guarded... -kevinh
    @scope.$apply(() =>
      key = @vehicleKey(data.missionId)
      v = @scope.vehicleMarkers[key]
      if v? # We only update heading if we already have an object with position
        v.iconAngle = data.payload.yaw
        # oops - angular-leaflet only reads options at the time the marker is created, either we need to manage our own
        # markers (probably a good idea) or we need to recreate the marker to get the new heading to show up.
        #<EMAIL>.debug("Can't set angle to #{v.iconAngle} because angular-leaflet is dumb and separates options from markers")
    )

  onMissionUpdate: (data) =>
    @scope.$apply(() =>
      key = @vehicleKey(data.missionId)
      payload = data.payload
      lat = payload.mission.latitude
      lon = payload.mission.longitude
      if lat? && lon?
        v = @updateVehicle(key, lat, lon)
        @updateMarkerPopup(v, payload.mission)
    )

  onMissionEnd: (data) =>
    vehicleKey = @vehicleKey(data.missionId)
    delete @scope.vehicleMarkers[vehicleKey]
    delete @scope.vehiclePaths[vehicleKey]

  # Returns the marker
  updateVehicle: (vehicleKey, lat, lon) =>
    # We don't add new vehicles to the hashtable until fully inited (because I think angular has hooks watching for writes to @scope)
    v = @scope.vehicleMarkers[vehicleKey] ? {}
    v.payload ?= {} # Provide a placeholder empty set of payload fields
    v.lat = lat
    v.lng = lon
    v.focus = false
    v.draggable = false
    isLive = v.payload.isLive ? true # If we haven't yet received the mission object assume live
    # direction of arrow on icon should change depending on direction

    loginName = @authService.getUser()?.login
    isMine = loginName == v.payload?.userName
    v.icon =
        iconUrl:
          if isMine
            v.payload?.userAvatarImage + '?d=mm'
          else if isLive
            'images/vehicle-marker-active.png'
          else
            'images/vehicle-marker-inactive.png'
        iconSize: [35, 35] #size of the icon<commit_msg>show user avatar as the icon for their vehicles on live map<commit_after># FIXME - fetch kml from here http://localhost:8080/api/v1/mission/10/messages.kml
class MapController
  @$inject: ['$scope', '$http']
  constructor: (@scope, @http) ->
    @initMap()

  initMap: =>
    # Collect various maps worth using
    mbox = (key) ->
      url: "https://a.tiles.mapbox.com/v3/" + key + "/{z}/{x}/{y}.png"
      options:
        attribution: '<a href="http://www.mapbox.com/about/maps/" target="_blank">Terms &amp; Feedback</a>'

    maps =
      openstreetmap:
        url: "http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
        options:
          attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
      opencyclemap:
        url: "http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png",
        options:
          attribution: 'All maps &copy; <a href="http://www.opencyclemap.org">OpenCycleMap</a>, map data &copy; <a href="http://www.openstreetmap.org">OpenStreetMap</a> (<a href="http://www.openstreetmap.org/copyright">ODbL</a>'
      mapbox_bright: mbox("mapbox.world-bright")
      mapbox_example: mbox("examples.map-zr0njcqy")
      threedr_default: mbox("kevin3dr.hokdl9ko")

    @scope.defaults =
      scrollWheelZoom: true
      zoom: 10
      minZoom: 2
    @scope.tiles = maps.threedr_default

class BoundsFactory
  constructor: () ->
    @bounds =
      northEast:
        lat: -Number.MAX_VALUE
        lng: -Number.MAX_VALUE
      southWest:
        lat: Number.MAX_VALUE
        lng: Number.MAX_VALUE

  # expand bounding box to include pt
  # returns true if we changed the bounding box
  expand: (lat, lon) ->
    # Updating bounds are _very_ expensive in leaflet, so we try to 'bucket' our bounds in .1 deg increments
    roundUp = (x) -> Math.ceil(x * 10) / 10.0
    roundDown = (x) -> Math.floor(x * 10) / 10.0

    dirty = false

    # We use the dirty flag to avoid changing scope object if nothing changed
    min = (newval, old) ->
      if newval < old
        dirty = true
        newval
      else
        old

    max = (newval, old) ->
      if newval > old
        dirty = true
        newval
      else
        old

    @bounds.southWest.lat = min(roundDown(lat),@bounds.southWest.lat)
    @bounds.southWest.lng = min(roundDown(lon), @bounds.southWest.lng)
    @bounds.northEast.lat = max(roundUp(lat), @bounds.northEast.lat)
    @bounds.northEast.lng = max(roundUp(lon), @bounds.northEast.lng)
    dirty

class LiveMapController extends MapController
  @$inject: ['$scope', '$log', 'leafletData', '$http', 'missionService', 'authService']
  constructor: (scope, @log, leafletData, http, @missionService, @authService) ->
    scope.leafletData = leafletData
    @boundsFactory = new BoundsFactory

    @count = 0
    scope.vehicleMarkers = {}
    scope.vehiclePaths = {}
    scope.bounds = {} # Angular wants to see at least an empty object (not undefined)
    scope.center = {}

    # TODO: fake markers arn't workng
    #scope.vehicleMarkers['missionId_fake'] = {lat: scope.hawaii.lat, lng: scope.hawwai.lng, focus: false, draggable: false}

    super(scope, http)

    s = @missionService

    listeners =
      "loc": @onLive
      "att": @onAttitude
      "start": @onMissionUpdate
      "end": @onMissionEnd
      "mode": @updateVehicleMessage
      "arm": @updateVehicleMessage
      "update": @onMissionUpdate
      "user": @onMissionUpdate
      "mystery": @updateVehicleMessage
      "text": @updateVehicleMessage

    listenerIds = s.atmosphere.on(name, callback) for name, callback of listeners
    @log.debug('live map now subscribed')

    # We bounce our atmosphere link, because the server will automatically resend vehicle messages on each new connection
    # (And our controller is not keeping a list of past connection messages - FIXME, it would be better to keep
    # a model object to preserve such state)
    s.atmosphere_connect()

    scope.$on("$destroy", () =>
      @log.debug('Unsubscribe for atmosphere notification')
      for id of listenerIds
        s.atmosphere.off(id)
      s.atmosphere_disconnect()
    )

  onLive: (data) =>
    # Apply should be called around the function to be guarded... -kevinh
    @scope.$apply(() =>
      key = @vehicleKey(data.missionId)
      @updateVehicle(key, data.payload.lat, data.payload.lon)

      # Grow (or create) our bounds - We don't really use this feature much any more because starting with the whole world looks better
      #if @boundsFactory.expand(data.payload.lat, data.payload.lon)
      #  @scope.bounds = @boundsFactory.bounds
    )

  onAttitude: (data) =>
    # FIXME - not sure if I need apply... (or how to optimize it)
    # Apply should be called around the function to be guarded... -kevinh
    @scope.$apply(() =>
      key = @vehicleKey(data.missionId)
      v = @scope.vehicleMarkers[key]
      if v? # We only update heading if we already have an object with position
        v.iconAngle = data.payload.yaw
        # oops - angular-leaflet only reads options at the time the marker is created, either we need to manage our own
        # markers (probably a good idea) or we need to recreate the marker to get the new heading to show up.
        #<EMAIL>.debug("Can't set angle to #{v.iconAngle} because angular-leaflet is dumb and separates options from markers")
    )

  onMissionUpdate: (data) =>
    @scope.$apply(() =>
      key = @vehicleKey(data.missionId)
      payload = data.payload
      lat = payload.latitude
      lon = payload.longitude
      if lat? && lon?
        v = @updateVehicle(key, lat, lon, payload)
        @updateMarkerPopup(v, payload)
    )

  onMissionEnd: (data) =>
    vehicleKey = @vehicleKey(data.missionId)
    delete @scope.vehicleMarkers[vehicleKey]
    delete @scope.vehiclePaths[vehicleKey]

  # Returns the marker
  updateVehicle: (vehicleKey, lat, lon, newMission) =>
    # We don't add new vehicles to the hashtable until fully inited (because I think angular has hooks watching for writes to @scope)
    v = @scope.vehicleMarkers[vehicleKey] ? {}

    v.payload ?= {} # Provide a placeholder empty set of payload fields
    if newMission? # If we have a new mission use it to extend our payload
      angular.extend(v.payload, newMission)

    v.lat = lat
    v.lng = lon
    v.focus = false
    v.draggable = false
    mission = v.payload
    isLive = mission?.isLive ? true # If we haven't yet received the mission object assume live
    # direction of arrow on icon should change depending on direction

    loginName = @authService.getUser()?.login
    isMine = loginName == mission?.userName
    @log.debug("#{mission?.userName} #{mission?.id} vs #{loginName} isMine=#{isMine}")
    v.icon =
        iconUrl:
          if isMine
            mission.userAvatarImage + '?d=mm'
          else if isLive
            'images/vehicle-marker-active.png'
          else
            'images/vehicle-marker-inactive.png'
        iconSize: [35, 35] #size of the icon
{'fraction_non_alphanumeric': 0.11955967048188379, 'fraction_numerical': 0.004374134285922578, 'mean_word_length': 2.82649930264993, 'pattern_counts': {'":': 20, '<': 27, '<?xml version=': 0, '>': 65, 'https://': 2, 'lorem ipsum': 0, 'www.': 10, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5098199', 'n_tokens_mistral': 4414, 'n_tokens_neox': 4215, 'n_words': 1233}