text: #!/usr/bin/env python
  
  # Copyright (c) 2019 Computer Vision Center (CVC) at the Universitat Autonoma de
  # Barcelona (UAB).
  #
  # This work is licensed under the terms of the MIT license.
  # For a copy, see <https://opensource.org/licenses/MIT>.
  
  # Allows visualising a 2D map generated by vehicles.
  
  """
  Welcome to CARLA No-Rendering Mode Visualizer
  
      TAB          : toggle hero mode
      Mouse Wheel  : zoom in / zoom out
      Mouse Drag   : move map (map mode only)
  
      W            : throttle
      S            : brake
      AD           : steer
      Q            : toggle reverse
      Space        : hand-brake
      P            : toggle autopilot
      M            : toggle manual transmission
      ,/.          : gear up/down
  
      F1           : toggle HUD
      I            : toggle actor ids
      H/?          : toggle help
      ESC          : quit
  """
  
  # ==============================================================================
  # -- find carla module ---------------------------------------------------------
  # ==============================================================================
  
  import glob
  import os
  import sys
  
  try:
      sys.path.append(glob.glob('../carla/dist/carla-*%d.%d-%s.egg' % (
          sys.version_info.major,
          sys.version_info.minor,
          'win-amd64' if os.name == 'nt' else 'linux-x86_64'))[0])
  except IndexError:
      pass
  
  # ==============================================================================
  # -- imports -------------------------------------------------------------------
  # ==============================================================================
  
  import carla
  from carla import TrafficLightState as tls
  
  import argparse
  import logging
  import datetime
  import weakref
  import math
  import random
  
  try:
      import pygame
      from pygame.locals import KMOD_CTRL
      from pygame.locals import KMOD_SHIFT
      from pygame.locals import K_COMMA
      from pygame.locals import K_DOWN
      from pygame.locals import K_ESCAPE
      from pygame.locals import K_F1
      from pygame.locals import K_LEFT
      from pygame.locals import K_PERIOD
      from pygame.locals import K_RIGHT
      from pygame.locals import K_SLASH
      from pygame.locals import K_SPACE
      from pygame.locals import K_TAB
      from pygame.locals import K_UP
      from pygame.locals import K_a
      from pygame.locals import K_d
      from pygame.locals import K_h
      from pygame.locals import K_i
      from pygame.locals import K_m
      from pygame.locals import K_p
      from pygame.locals import K_q
      from pygame.locals import K_s
      from pygame.locals import K_w
  except ImportError:
      raise RuntimeError('cannot import pygame, make sure pygame package is installed')
  
  # ==============================================================================
  # -- Constants -----------------------------------------------------------------
  # ==============================================================================
  
  # Colors
  
  # We will use the color palette used in Tango Desktop Project (Each color is indexed depending on brightness level)
  # See: https://en.wikipedia.org/wiki/Tango_Desktop_Project
  
  COLOR_BUTTER_0 = pygame.Color(252, 233, 79)
  COLOR_BUTTER_1 = pygame.Color(237, 212, 0)
  COLOR_BUTTER_2 = pygame.Color(196, 160, 0)
  
  COLOR_ORANGE_0 = pygame.Color(252, 175, 62)
  COLOR_ORANGE_1 = pygame.Color(245, 121, 0)
  COLOR_ORANGE_2 = pygame.Color(209, 92, 0)
  
  COLOR_CHOCOLATE_0 = pygame.Color(233, 185, 110)
  COLOR_CHOCOLATE_1 = pygame.Color(193, 125, 17)
  COLOR_CHOCOLATE_2 = pygame.Color(143, 89, 2)
  
  COLOR_CHAMELEON_0 = pygame.Color(138, 226, 52)
  COLOR_CHAMELEON_1 = pygame.Color(115, 210, 22)
  COLOR_CHAMELEON_2 = pygame.Color(78, 154, 6)
  
  COLOR_SKY_BLUE_0 = pygame.Color(114, 159, 207)
  COLOR_SKY_BLUE_1 = pygame.Color(52, 101, 164)
  COLOR_SKY_BLUE_2 = pygame.Color(32, 74, 135)
  
  COLOR_PLUM_0 = pygame.Color(173, 127, 168)
  COLOR_PLUM_1 = pygame.Color(117, 80, 123)
  COLOR_PLUM_2 = pygame.Color(92, 53, 102)
  
  COLOR_SCARLET_RED_0 = pygame.Color(239, 41, 41)
  COLOR_SCARLET_RED_1 = pygame.Color(204, 0, 0)
  COLOR_SCARLET_RED_2 = pygame.Color(164, 0, 0)
  
  COLOR_ALUMINIUM_0 = pygame.Color(238, 238, 236)
  COLOR_ALUMINIUM_1 = pygame.Color(211, 215, 207)
  COLOR_ALUMINIUM_2 = pygame.Color(186, 189, 182)
  COLOR_ALUMINIUM_3 = pygame.Color(136, 138, 133)
  COLOR_ALUMINIUM_4 = pygame.Color(85, 87, 83)
  COLOR_ALUMINIUM_4_5 = pygame.Color(66, 62, 64)
  COLOR_ALUMINIUM_5 = pygame.Color(46, 52, 54)
  
  
  COLOR_WHITE = pygame.Color(255, 255, 255)
  COLOR_BLACK = pygame.Color(0, 0, 0)
  
  
  # Module Defines
  MODULE_WORLD = 'WORLD'
  MODULE_HUD = 'HUD'
  MODULE_INPUT = 'INPUT'
  
  PIXELS_PER_METER = 12
  
  MAP_DEFAULT_SCALE = 0.1
  HERO_DEFAULT_SCALE = 1.0
  
  PIXELS_AHEAD_VEHICLE = 150
  
  # ==============================================================================
  # -- Util -----------------------------------------------------------
  # ==============================================================================
  
  
  def get_actor_display_name(actor, truncate=250):
      name = ' '.join(actor.type_id.replace('_', '.').title().split('.')[1:])
      return (name[:truncate - 1] + u'\u2026') if len(name) > truncate else name
  
  
  class Util(object):
  
      @staticmethod
      def blits(destination_surface, source_surfaces, rect=None, blend_mode=0):
          for surface in source_surfaces:
              destination_surface.blit(surface[0], surface[1], rect, blend_mode)
  
      @staticmethod
      def length(v):
          return math.sqrt(v.x**2 + v.y**2 + v.z**2)
  
      @staticmethod
      def get_bounding_box(actor):
          bb = actor.trigger_volume.extent
          corners = [carla.Location(x=-bb.x, y=-bb.y),
                     carla.Location(x=bb.x, y=-bb.y),
                     carla.Location(x=bb.x, y=bb.y),
                     carla.Location(x=-bb.x, y=bb.y),
                     carla.Location(x=-bb.x, y=-bb.y)]
          corners = [x + actor.trigger_volume.location for x in corners]
          t = actor.get_transform()
          t.transform(corners)
          return corners
  
  # ==============================================================================
  # -- ModuleManager -------------------------------------------------------------
  # ==============================================================================
  
  
  class ModuleManager(object):
      def __init__(self):
          self.modules = []
  
      def register_module(self, module):
          self.modules.append(module)
  
      def clear_modules(self):
          del self.modules[:]
  
      def tick(self, clock):
          # Update all the modules
          for module in self.modules:
              module.tick(clock)
  
      def render(self, display):
          display.fill(COLOR_ALUMINIUM_4)
          for module in self.modules:
              module.render(display)
  
      def get_module(self, name):
          for module in self.modules:
              if module.name == name:
                  return module
  
      def start_modules(self):
          for module in self.modules:
              module.start()
  
  
  # ==============================================================================
  # -- FadingText ----------------------------------------------------------------
  # ==============================================================================
  
  
  class FadingText(object):
      def __init__(self, font, dim, pos):
          self.font = font
          self.dim = dim
          self.pos = pos
          self.seconds_left = 0
          self.surface = pygame.Surface(self.dim)
  
      def set_text(self, text, color=COLOR_WHITE, seconds=2.0):
          text_texture = self.font.render(text, True, color)
          self.surface = pygame.Surface(self.dim)
          self.seconds_left = seconds
          self.surface.fill(COLOR_BLACK)
          self.surface.blit(text_texture, (10, 11))
  
      def tick(self, clock):
          delta_seconds = 1e-3 * clock.get_time()
          self.seconds_left = max(0.0, self.seconds_left - delta_seconds)
          self.surface.set_alpha(500.0 * self.seconds_left)
  
      def render(self, display):
          display.blit(self.surface, self.pos)
  
  
  # ==============================================================================
  # -- HelpText ------------------------------------------------------------------
  # ==============================================================================
  
  
  class HelpText(object):
      def __init__(self, font, width, height):
          lines = __doc__.split('\n')
          self.font = font
          self.dim = (680, len(lines) * 22 + 12)
          self.pos = (0.5 * width - 0.5 * self.dim[0], 0.5 * height - 0.5 * self.dim[1])
          self.seconds_left = 0
          self.surface = pygame.Surface(self.dim)
          self.surface.fill(COLOR_BLACK)
          for n, line in enumerate(lines):
              text_texture = self.font.render(line, True, COLOR_WHITE)
              self.surface.blit(text_texture, (22, n * 22))
              self._render = False
          self.surface.set_alpha(220)
  
      def toggle(self):
          self._render = not self._render
  
      def render(self, display):
          if self._render:
              display.blit(self.surface, self.pos)
  
  
  # ==============================================================================
  # -- ModuleHUD -----------------------------------------------------------------
  # ==============================================================================
  
  
  class ModuleHUD (object):
  
      def __init__(self, name, width, height):
          self.name = name
          self.dim = (width, height)
          self._init_hud_params()
          self._init_data_params()
  
      def start(self):
          pass
  
      def _init_hud_params(self):
          fonts = [x for x in pygame.font.get_fonts() if 'mono' in x]
          default_font = 'ubuntumono'
          mono = default_font if default_font in fonts else fonts[0]
          mono = pygame.font.match_font(mono)
          self._font_mono = pygame.font.Font(mono, 14)
          self._header_font = pygame.font.SysFont('Arial', 14, True)
          self.help = HelpText(pygame.font.Font(mono, 24), *self.dim)
          self._notifications = FadingText(
              pygame.font.Font(pygame.font.get_default_font(), 20),
              (self.dim[0], 40), (0, self.dim[1] - 40))
  
      def _init_data_params(self):
          self.show_info = True
          self.show_actor_ids = False
          self._info_text = {}
  
      def notification(self, text, seconds=2.0):
          self._notifications.set_text(text, seconds=seconds)
  
      def tick(self, clock):
          self._notifications.tick(clock)
  
      def add_info(self, module_name, info):
          self._info_text[module_name] = info
  
      def render_vehicles_ids(self, vehicle_id_surface, list_actors, world_to_pixel, hero_actor, hero_transform):
          vehicle_id_surface.fill(COLOR_BLACK)
          if self.show_actor_ids:
              vehicle_id_surface.set_alpha(150)
              for actor in list_actors:
                  x, y = world_to_pixel(actor[1].location)
  
                  angle = 0
                  if hero_actor is not None:
                      angle = -hero_transform.rotation.yaw - 90
  
                  color = COLOR_SKY_BLUE_0
                  if int(actor[0].attributes['number_of_wheels']) == 2:
                      color = COLOR_CHOCOLATE_0
                  if actor[0].attributes['role_name'] == 'hero':
                      color = COLOR_CHAMELEON_0
  
                  font_surface = self._header_font.render(str(actor[0].id), True, color)
                  rotated_font_surface = pygame.transform.rotate(font_surface, angle)
                  rect = rotated_font_surface.get_rect(center=(x, y))
                  vehicle_id_surface.blit(rotated_font_surface, rect)
  
          return vehicle_id_surface
  
      def render(self, display):
          if self.show_info:
              info_surface = pygame.Surface((240, self.dim[1]))
              info_surface.set_alpha(100)
              display.blit(info_surface, (0, 0))
              v_offset = 4
              bar_h_offset = 100
              bar_width = 106
              i = 0
              for module_name, module_info in self._info_text.items():
                  if not module_info:
                      continue
                  surface = self._header_font.render(module_name, True, COLOR_ALUMINIUM_0).convert_alpha()
                  display.blit(surface, (8 + bar_width / 2, 18 * i + v_offset))
                  v_offset += 12
                  i += 1
                  for item in module_info:
                      if v_offset + 18 > self.dim[1]:
                          break
                      if isinstance(item, list):
                          if len(item) > 1:
                              points = [(x + 8, v_offset + 8 + (1.0 - y) * 30) for x, y in enumerate(item)]
                              pygame.draw.lines(display, (255, 136, 0), False, points, 2)
                          item = None
                      elif isinstance(item, tuple):
                          if isinstance(item[1], bool):
                              rect = pygame.Rect((bar_h_offset, v_offset + 8), (6, 6))
                              pygame.draw.rect(display, COLOR_ALUMINIUM_0, rect, 0 if item[1] else 1)
                          else:
                              rect_border = pygame.Rect((bar_h_offset, v_offset + 8), (bar_width, 6))
                              pygame.draw.rect(display, COLOR_ALUMINIUM_0, rect_border, 1)
                              f = (item[1] - item[2]) / (item[3] - item[2])
                              if item[2] < 0.0:
                                  rect = pygame.Rect((bar_h_offset + f * (bar_width - 6), v_offset + 8), (6, 6))
                              else:
                                  rect = pygame.Rect((bar_h_offset, v_offset + 8), (f * bar_width, 6))
                              pygame.draw.rect(display, COLOR_ALUMINIUM_0, rect)
                          item = item[0]
                      if item:  # At this point has to be a str.
                          surface = self._font_mono.render(item, True, COLOR_ALUMINIUM_0).convert_alpha()
                          display.blit(surface, (8, 18 * i + v_offset))
                      v_offset += 18
                  v_offset += 24
          self._notifications.render(display)
          self.help.render(display)
  
  
  # ==============================================================================
  # -- TrafficLightSurfaces ------------------------------------------------------
  # ==============================================================================
  
  
  class TrafficLightSurfaces(object):
      """Holds the surfaces (scaled and rotated) for painting traffic lights"""
  
      def __init__(self):
          def make_surface(tl):
              w = 40
              surface = pygame.Surface((w, 3 * w), pygame.SRCALPHA)
              surface.fill(COLOR_ALUMINIUM_5 if tl != 'h' else COLOR_ORANGE_2)
              if tl != 'h':
                  hw = int(w / 2)
                  off = COLOR_ALUMINIUM_4
                  red = COLOR_SCARLET_RED_0
                  yellow = COLOR_BUTTER_0
                  green = COLOR_CHAMELEON_0
                  pygame.draw.circle(surface, red if tl == tls.Red else off, (hw, hw), int(0.4 * w))
                  pygame.draw.circle(surface, yellow if tl == tls.Yellow else off, (hw, w + hw), int(0.4 * w))
                  pygame.draw.circle(surface, green if tl == tls.Green else off, (hw, 2 * w + hw), int(0.4 * w))
              return pygame.transform.smoothscale(surface, (15, 45) if tl != 'h' else (19, 49))
          self._original_surfaces = {
              'h': make_surface('h'),
              tls.Red: make_surface(tls.Red),
              tls.Yellow: make_surface(tls.Yellow),
              tls.Green: make_surface(tls.Green),
              tls.Off: make_surface(tls.Off),
              tls.Unknown: make_surface(tls.Unknown)
          }
          self.surfaces = dict(self._original_surfaces)
  
      def rotozoom(self, angle, scale):
          for key, surface in self._original_surfaces.items():
              self.surfaces[key] = pygame.transform.rotozoom(surface, angle, scale)
  
  
  # ==============================================================================
  # -- World ---------------------------------------------------------------------
  # ==============================================================================
  
  
  class MapImage(object):
      def __init__(self, carla_world, carla_map, pixels_per_meter, show_triggers, show_connections, show_spawn_points):
          self._pixels_per_meter = pixels_per_meter
          self.scale = 1.0
          self.show_triggers = show_triggers
          self.show_connections = show_connections
          self.show_spawn_points = True#show_spawn_points
  
          waypoints = carla_map.generate_waypoints(2)
          margin = 50
          max_x = max(waypoints, key=lambda x: x.transform.location.x).transform.location.x + margin
          max_y = max(waypoints, key=lambda x: x.transform.location.y).transform.location.y + margin
          min_x = min(waypoints, key=lambda x: x.transform.location.x).transform.location.x - margin
          min_y = min(waypoints, key=lambda x: x.transform.location.y).transform.location.y - margin
  
          self.width = max(max_x - min_x, max_y - min_y)
          self._world_offset = (min_x, min_y)
  
          width_in_pixels = int(self._pixels_per_meter * self.width)
  
          self.big_map_surface = pygame.Surface((width_in_pixels, width_in_pixels)).convert()
          self.draw_road_map(self.big_map_surface, carla_world, carla_map, self.world_to_pixel, self.world_to_pixel_width)
          self.surface = self.big_map_surface
  
      def draw_road_map(self, map_surface, carla_world, carla_map, world_to_pixel, world_to_pixel_width):
          map_surface.fill(COLOR_ALUMINIUM_4)
          precision = 0.05
  
          def lane_marking_color_to_tango(lane_marking_color):
              tango_color = COLOR_BLACK
  
              if lane_marking_color == carla.LaneMarkingColor.White:
                  tango_color = COLOR_ALUMINIUM_2
  
              elif lane_marking_color == carla.LaneMarkingColor.Blue:
                  tango_color = COLOR_SKY_BLUE_0
  
              elif lane_marking_color == carla.LaneMarkingColor.Green:
                  tango_color = COLOR_CHAMELEON_0
  
              elif lane_marking_color == carla.LaneMarkingColor.Red:
                  tango_color = COLOR_SCARLET_RED_0
  
              elif lane_marking_color == carla.LaneMarkingColor.Yellow:
                  tango_color = COLOR_ORANGE_0
  
              return tango_color
  
          def draw_solid_line(surface, color, closed, points, width):
              if len(points) >= 2:
                  pygame.draw.lines(surface, color, closed, points, width)
  
          def draw_broken_line(surface, color, closed, points, width):
              broken_lines = [x for n, x in enumerate(zip(*(iter(points),) * 20)) if n % 3 == 0]
              for line in broken_lines:
                  pygame.draw.lines(surface, color, closed, line, width)
  
          def get_lane_markings(lane_marking_type, lane_marking_color, waypoints, sign):
              margin = 0.20
              if lane_marking_type == carla.LaneMarkingType.Broken or (lane_marking_type == carla.LaneMarkingType.Solid):
                  marking_1 = [world_to_pixel(lateral_shift(w.transform, sign * w.lane_width * 0.5)) for w in waypoints]
                  return [(lane_marking_type, lane_marking_color, marking_1)]
              elif lane_marking_type == carla.LaneMarkingType.SolidBroken or lane_marking_type == carla.LaneMarkingType.BrokenSolid:
                  marking_1 = [world_to_pixel(lateral_shift(w.transform, sign * w.lane_width * 0.5)) for w in waypoints]
                  marking_2 = [world_to_pixel(lateral_shift(w.transform,
                                                            sign * (w.lane_width * 0.5 + margin * 2))) for w in waypoints]
                  return [(carla.LaneMarkingType.Solid, lane_marking_color, marking_1),
                          (carla.LaneMarkingType.Broken, lane_marking_color, marking_2)]
              elif lane_marking_type == carla.LaneMarkingType.BrokenBroken:
                  marking = [world_to_pixel(lateral_shift(w.transform,
                                                          sign * (w.lane_width * 0.5 - margin))) for w in waypoints]
                  return [(carla.LaneMarkingType.Broken, lane_marking_color, marking)]
              elif lane_marking_type == carla.LaneMarkingType.SolidSolid:
                  marking = [world_to_pixel(lateral_shift(w.transform,
                                                          sign * ((w.lane_width * 0.5) - margin))) for w in waypoints]
                  return [(carla.LaneMarkingType.Solid, lane_marking_color, marking)]
  
              return [(carla.LaneMarkingType.NONE, carla.LaneMarkingColor.Other, [])]
  
          def draw_lane_marking(surface, waypoints, is_left):
              sign = -1 if is_left else 1
              lane_marking = None
  
              marking_type = carla.LaneMarkingType.NONE
              previous_marking_type = carla.LaneMarkingType.NONE
  
              marking_color = carla.LaneMarkingColor.Other
              previous_marking_color = carla.LaneMarkingColor.Other
  
              waypoints_list = []
              temp_waypoints = []
              current_lane_marking = carla.LaneMarkingType.NONE
              for sample in waypoints:
                  lane_marking = sample.left_lane_marking if sign < 0 else sample.right_lane_marking
  
                  if lane_marking is None:
                      continue
  
                  marking_type = lane_marking.type
                  marking_color = lane_marking.color
  
                  if current_lane_marking != marking_type:
                      markings = get_lane_markings(
                          previous_marking_type,
                          lane_marking_color_to_tango(previous_marking_color),
                          temp_waypoints,
                          sign)
                      current_lane_marking = marking_type
  
                      for marking in markings:
                          waypoints_list.append(marking)
  
                      temp_waypoints = temp_waypoints[-1:]
  
                  else:
                      temp_waypoints.append((sample))
                      previous_marking_type = marking_type
                      previous_marking_color = marking_color
  
              # Add last marking
              last_markings = get_lane_markings(
                  previous_marking_type,
                  lane_marking_color_to_tango(previous_marking_color),
                  temp_waypoints,
                  sign)
              for marking in last_markings:
                  waypoints_list.append(marking)
  
              for markings in waypoints_list:
                  if markings[0] == carla.LaneMarkingType.Solid:
                      draw_solid_line(surface, markings[1], False, markings[2], 2)
                  elif markings[0] == carla.LaneMarkingType.Broken:
                      draw_broken_line(surface, markings[1], False, markings[2], 2)
  
          def draw_arrow(surface, transform, color=COLOR_ALUMINIUM_2):
              transform.rotation.yaw += 180
              forward = transform.get_forward_vector()
              transform.rotation.yaw += 90
              right_dir = transform.get_forward_vector()
              end = transform.location
              start = end - 2.0 * forward
              right = start + 0.8 * forward + 0.4 * right_dir
              left = start + 0.8 * forward - 0.4 * right_dir
              pygame.draw.lines(
                  surface, color, False, [
                      world_to_pixel(x) for x in [
                          start, end]], 4)
              pygame.draw.lines(
                  surface, color, False, [
                      world_to_pixel(x) for x in [
                          left, start, right]], 4)
          
          def draw_spawn_points(surface, color=COLOR_SCARLET_RED_0):
              spawn_points = carla_map.get_spawn_points()
              font_size = world_to_pixel_width(2)
              font = pygame.font.SysFont('Arial', font_size)
              for i in range(len(spawn_points)):
                  sp = spawn_points[i]
  
                  spawn_point_font_surface = font.render(str(i), False, color)
                  spawn_point_font_surface = pygame.transform.scale(
                      spawn_point_font_surface, (spawn_point_font_surface.get_width(), spawn_point_font_surface.get_height() * 2))
                  pixel_pos = world_to_pixel(sp.location)
                  offset = spawn_point_font_surface.get_rect(center=(pixel_pos[0], pixel_pos[1]))
                  surface.blit(spawn_point_font_surface, offset)
  
          def draw_traffic_signs(surface, font_surface, actor, color=COLOR_ALUMINIUM_2, trigger_color=COLOR_PLUM_0):
              transform = actor.get_transform()
              waypoint = carla_map.get_waypoint(transform.location)
  
              angle = -waypoint.transform.rotation.yaw - 90.0
              font_surface = pygame.transform.rotate(font_surface, angle)
              pixel_pos = world_to_pixel(waypoint.transform.location)
              offset = font_surface.get_rect(center=(pixel_pos[0], pixel_pos[1]))
              surface.blit(font_surface, offset)
  
              # Draw line in front of stop
              forward_vector = carla.Location(waypoint.transform.get_forward_vector())
              left_vector = carla.Location(-forward_vector.y, forward_vector.x,
                                           forward_vector.z) * waypoint.lane_width / 2 * 0.7
  
              line = [(waypoint.transform.location + (forward_vector * 1.5) + (left_vector)),
                      (waypoint.transform.location + (forward_vector * 1.5) - (left_vector))]
  
              line_pixel = [world_to_pixel(p) for p in line]
              pygame.draw.lines(surface, color, True, line_pixel, 2)
  
              # draw bounding box
              if self.show_triggers:
                  corners = Util.get_bounding_box(actor)
                  corners = [world_to_pixel(p) for p in corners]
                  pygame.draw.lines(surface, trigger_color, True, corners, 2)
  
          def lateral_shift(transform, shift):
              transform.rotation.yaw += 90
              return transform.location + shift * transform.get_forward_vector()
  
          def draw_topology(carla_topology, index):
              topology = [x[index] for x in carla_topology]
              topology = sorted(topology, key=lambda w: w.transform.location.z)
              for waypoint in topology:
                  # if waypoint.road_id == 150 or waypoint.road_id == 16:
                  waypoints = [waypoint]
  
                  nxt = waypoint.next(precision)
                  if len(nxt) > 0:
                      nxt = nxt[0]
                      while nxt.road_id == waypoint.road_id:
                          waypoints.append(nxt)
                          nxt = nxt.next(precision)
                          if len(nxt) > 0:
                              nxt = nxt[0]
                          else:
                              break
  
                  # Draw Road
                  road_left_side = [lateral_shift(w.transform, -w.lane_width * 0.5) for w in waypoints]
                  road_right_side = [lateral_shift(w.transform, w.lane_width * 0.5) for w in waypoints]
  
                  polygon = road_left_side + [x for x in reversed(road_right_side)]
                  polygon = [world_to_pixel(x) for x in polygon]
  
                  if len(polygon) > 2:
                      pygame.draw.polygon(map_surface, COLOR_ALUMINIUM_5, polygon, 5)
                      pygame.draw.polygon(map_surface, COLOR_ALUMINIUM_5, polygon)
  
                  # Draw Shoulders and Parkings
                  PARKING_COLOR = COLOR_ALUMINIUM_4_5
                  SHOULDER_COLOR = COLOR_ALUMINIUM_5
  
                  final_color = SHOULDER_COLOR
  
                  # Draw Right
                  shoulder = []
                  for w in waypoints:
                      r = w.get_right_lane()
                      if r is not None and (
                              r.lane_type == carla.LaneType.Shoulder or r.lane_type == carla.LaneType.Parking):
                          if r.lane_type == carla.LaneType.Parking:
                              final_color = PARKING_COLOR
                          shoulder.append(r)
  
                  shoulder_left_side = [lateral_shift(w.transform, -w.lane_width * 0.5) for w in shoulder]
                  shoulder_right_side = [lateral_shift(w.transform, w.lane_width * 0.5) for w in shoulder]
  
                  polygon = shoulder_left_side + [x for x in reversed(shoulder_right_side)]
                  polygon = [world_to_pixel(x) for x in polygon]
  
                  if len(polygon) > 2:
                      pygame.draw.polygon(map_surface, final_color, polygon, 5)
                      pygame.draw.polygon(map_surface, final_color, polygon)
  
                  draw_lane_marking(
                      map_surface,
                      shoulder,
                      False)
  
                  # Draw Left
                  shoulder = []
                  for w in waypoints:
                      r = w.get_left_lane()
                      if r is not None and (
                              r.lane_type == carla.LaneType.Shoulder or r.lane_type == carla.LaneType.Parking):
                          if r.lane_type == carla.LaneType.Parking:
                              final_color = PARKING_COLOR
                          shoulder.append(r)
  
                  shoulder_left_side = [lateral_shift(w.transform, -w.lane_width * 0.5) for w in shoulder]
                  shoulder_right_side = [lateral_shift(w.transform, w.lane_width * 0.5) for w in shoulder]
  
                  polygon = shoulder_left_side + [x for x in reversed(shoulder_right_side)]
                  polygon = [world_to_pixel(x) for x in polygon]
  
                  if len(polygon) > 2:
                      pygame.draw.polygon(map_surface, final_color, polygon, 5)
                      pygame.draw.polygon(map_surface, final_color, polygon)
  
                  draw_lane_marking(
                      map_surface,
                      shoulder,
                      True)
  
                  # Draw Lane Markings and Arrows
                  if not waypoint.is_intersection:
                      draw_lane_marking(
                          map_surface,
                          waypoints,
                          True)
                      draw_lane_marking(
                          map_surface,
                          waypoints,
                          False)
                      for n, wp in enumerate(waypoints):
                          if ((n + 1) % 400) == 0:
                              draw_arrow(map_surface, wp.transform)
  
          topology = carla_map.get_topology()
          draw_topology(topology, 0)
          draw_topology(topology, 1)
  
  
          if self.show_spawn_points:
              draw_spawn_points(map_surface)
  
          if self.show_connections:
              dist = 1.5
              to_pixel = lambda wp: world_to_pixel(wp.transform.location)
              for wp in carla_map.generate_waypoints(dist):
                  col = (0, 255, 255) if wp.is_intersection else (0, 255, 0)
                  for nxt in wp.next(dist):
                      pygame.draw.line(map_surface, col, to_pixel(wp), to_pixel(nxt), 2)
                  if wp.lane_change & carla.LaneChange.Right:
                      r = wp.get_right_lane()
                      if r and r.lane_type == carla.LaneType.Driving:
                          pygame.draw.line(map_surface, col, to_pixel(wp), to_pixel(r), 2)
                  if wp.lane_change & carla.LaneChange.Left:
                      l = wp.get_left_lane()
                      if l and l.lane_type == carla.LaneType.Driving:
                          pygame.draw.line(map_surface, col, to_pixel(wp), to_pixel(l), 2)
  
          actors = carla_world.get_actors()
  
          # Draw Traffic Signs
          font_size = world_to_pixel_width(1)
          font = pygame.font.SysFont('Arial', font_size, True)
  
          stops = [actor for actor in actors if 'stop' in actor.type_id]
          yields = [actor for actor in actors if 'yield' in actor.type_id]
  
          stop_font_surface = font.render("STOP", False, COLOR_ALUMINIUM_2)
          stop_font_surface = pygame.transform.scale(
              stop_font_surface, (stop_font_surface.get_width(), stop_font_surface.get_height() * 2))
  
          yield_font_surface = font.render("YIELD", False, COLOR_ALUMINIUM_2)
          yield_font_surface = pygame.transform.scale(
              yield_font_surface, (yield_font_surface.get_width(), yield_font_surface.get_height() * 2))
  
          for ts_stop in stops:
              draw_traffic_signs(map_surface, stop_font_surface, ts_stop, trigger_color=COLOR_SCARLET_RED_1)
  
          for ts_yield in yields:
              draw_traffic_signs(map_surface, yield_font_surface, ts_yield, trigger_color=COLOR_ORANGE_1)
  
      def world_to_pixel(self, location, offset=(0, 0)):
          x = self.scale * self._pixels_per_meter * (location.x - self._world_offset[0])
          y = self.scale * self._pixels_per_meter * (location.y - self._world_offset[1])
          return [int(x - offset[0]), int(y - offset[1])]
  
      def world_to_pixel_width(self, width):
          return int(self.scale * self._pixels_per_meter * width)
  
      def scale_map(self, scale):
          if scale != self.scale:
              self.scale = scale
              width = int(self.big_map_surface.get_width() * self.scale)
              self.surface = pygame.transform.smoothscale(self.big_map_surface, (width, width))
  
  
  class ModuleWorld(object):
      def __init__(self, name, args, timeout):
          self.client = None
          self.name = name
          self.args = args
          self.timeout = timeout
          self.server_fps = 0.0
          self.simulation_time = 0
          self.server_clock = pygame.time.Clock()
  
          # World data
          self.world = None
          self.town_map = None
          self.actors_with_transforms = []
          # Store necessary modules
          self.module_hud = None
          self.module_input = None
  
          self.surface_size = [0, 0]
          self.prev_scaled_size = 0
          self.scaled_size = 0
          # Hero actor
          self.hero_actor = None
          self.spawned_hero = None
          self.hero_transform = None
  
          self.scale_offset = [0, 0]
  
          self.vehicle_id_surface = None
          self.result_surface = None
  
          self.traffic_light_surfaces = TrafficLightSurfaces()
          self.affected_traffic_light = None
  
          # Map info
          self.map_image = None
          self.border_round_surface = None
          self.original_surface_size = None
          self.hero_surface = None
          self.actors_surface = None
  
      def _get_data_from_carla(self):
          try:
              self.client = carla.Client(self.args.host, self.args.port)
              self.client.set_timeout(self.timeout)
  
              if self.args.map is None:
                  world = self.client.get_world()
              else:
                  world = self.client.load_world(self.args.map)
  
              town_map = world.get_map()
              return (world, town_map)
  
          except RuntimeError as ex:
              logging.error(ex)
              exit_game()
  
      def start(self):
          self.world, self.town_map = self._get_data_from_carla()
  
          settings = self.world.get_settings()
          settings.no_rendering_mode = self.args.no_rendering
          self.world.apply_settings(settings)
  
          # Create Surfaces
          self.map_image = MapImage(
              carla_world=self.world,
              carla_map=self.town_map,
              pixels_per_meter=PIXELS_PER_METER,
              show_triggers=self.args.show_triggers,
              show_connections=self.args.show_connections,
              show_spawn_points=self.args.show_spawn_points)
  
          # Store necessary modules
          self.module_hud = module_manager.get_module(MODULE_HUD)
          self.module_input = module_manager.get_module(MODULE_INPUT)
  
          self.original_surface_size = min(self.module_hud.dim[0], self.module_hud.dim[1])
          self.surface_size = self.map_image.big_map_surface.get_width()
  
          self.scaled_size = int(self.surface_size)
          self.prev_scaled_size = int(self.surface_size)
  
          # Render Actors
          self.actors_surface = pygame.Surface((self.map_image.surface.get_width(), self.map_image.surface.get_height()))
          self.actors_surface.set_colorkey(COLOR_BLACK)
  
          self.vehicle_id_surface = pygame.Surface((self.surface_size, self.surface_size)).convert()
          self.vehicle_id_surface.set_colorkey(COLOR_BLACK)
  
          self.border_round_surface = pygame.Surface(self.module_hud.dim, pygame.SRCALPHA).convert()
          self.border_round_surface.set_colorkey(COLOR_WHITE)
          self.border_round_surface.fill(COLOR_BLACK)
  
          center_offset = (int(self.module_hud.dim[0] / 2), int(self.module_hud.dim[1] / 2))
          pygame.draw.circle(self.border_round_surface, COLOR_ALUMINIUM_1, center_offset, int(self.module_hud.dim[1] / 2))
          pygame.draw.circle(self.border_round_surface, COLOR_WHITE, center_offset, int((self.module_hud.dim[1] - 8) / 2))
  
          scaled_original_size = self.original_surface_size * (1.0 / 0.9)
          self.hero_surface = pygame.Surface((scaled_original_size, scaled_original_size)).convert()
  
          self.result_surface = pygame.Surface((self.surface_size, self.surface_size)).convert()
          self.result_surface.set_colorkey(COLOR_BLACK)
  
          # Start hero mode by default
          self.select_hero_actor()
          self.hero_actor.set_autopilot(False)
          self.module_input.wheel_offset = HERO_DEFAULT_SCALE
          self.module_input.control = carla.VehicleControl()
  
          weak_self = weakref.ref(self)
          self.world.on_tick(lambda timestamp: ModuleWorld.on_world_tick(weak_self, timestamp))
  
      def select_hero_actor(self):
          hero_vehicles = [actor for actor in self.world.get_actors(
          ) if 'vehicle' in actor.type_id and actor.attributes['role_name'] == 'hero']
          if len(hero_vehicles) > 0:
              self.hero_actor = random.choice(hero_vehicles)
              self.hero_transform = self.hero_actor.get_transform()
          """else:
              self._spawn_hero()"""
  
      def _spawn_hero(self):
          # Get a random blueprint.
          blueprint = random.choice(self.world.get_blueprint_library().filter(self.args.filter))
          blueprint.set_attribute('role_name', 'hero')
          if blueprint.has_attribute('color'):
              color = random.choice(blueprint.get_attribute('color').recommended_values)
              blueprint.set_attribute('color', color)
          # Spawn the player.
          while self.hero_actor is None:
              spawn_points = self.world.get_map().get_spawn_points()
              spawn_point = random.choice(spawn_points) if spawn_points else carla.Transform()
              self.hero_actor = self.world.try_spawn_actor(blueprint, spawn_point)
          self.hero_transform = self.hero_actor.get_transform()
  
          # Save it in order to destroy it when closing program
          self.spawned_hero = self.hero_actor
  
      def tick(self, clock):
          actors = self.world.get_actors()
          self.actors_with_transforms = [(actor, actor.get_transform()) for actor in actors]
          if self.hero_actor is not None:
              self.hero_transform = self.hero_actor.get_transform()
          self.update_hud_info(clock)
  
      def update_hud_info(self, clock):
          hero_mode_text = []
          if self.hero_actor is not None:
              hero_speed = self.hero_actor.get_velocity()
              hero_speed_text = 3.6 * math.sqrt(hero_speed.x ** 2 + hero_speed.y ** 2 + hero_speed.z ** 2)
  
              affected_traffic_light_text = 'None'
              if self.affected_traffic_light is not None:
                  state = self.affected_traffic_light.state
                  if state == carla.TrafficLightState.Green:
                      affected_traffic_light_text = 'GREEN'
                  elif state == carla.TrafficLightState.Yellow:
                      affected_traffic_light_text = 'YELLOW'
                  else:
                      affected_traffic_light_text = 'RED'
  
              affected_speed_limit_text = self.hero_actor.get_speed_limit()
  
              hero_mode_text = [
                  'Hero Mode:                 ON',
                  'Hero ID:              %7d' % self.hero_actor.id,
                  'Hero Vehicle:  %14s' % get_actor_display_name(self.hero_actor, truncate=14),
                  'Hero Speed:          %3d km/h' % hero_speed_text,
                  'Hero Affected by:',
                  '  Traffic Light: %12s' % affected_traffic_light_text,
                  '  Speed Limit:       %3d km/h' % affected_speed_limit_text
              ]
          else:
              hero_mode_text = ['Hero Mode:                OFF']
  
          self.server_fps = self.server_clock.get_fps()
          self.server_fps = 'inf' if self.server_fps == float('inf') else round(self.server_fps)
          module_info_text = [
              'Server:  % 16s FPS' % self.server_fps,
              'Client:  % 16s FPS' % round(clock.get_fps()),
              'Simulation Time: % 12s' % datetime.timedelta(seconds=int(self.simulation_time)),
              'Map Name:          %10s' % self.town_map.name,
          ]
  
          module_info_text = module_info_text
          module_hud = module_manager.get_module(MODULE_HUD)
          module_hud.add_info(self.name, module_info_text)
          module_hud.add_info('HERO', hero_mode_text)
  
      @staticmethod
      def on_world_tick(weak_self, timestamp):
          self = weak_self()
          if not self:
              return
  
          self.server_clock.tick()
          self.server_fps = self.server_clock.get_fps()
          self.simulation_time = timestamp.elapsed_seconds
  
      def _split_actors(self):
          vehicles = []
          traffic_lights = []
          speed_limits = []
          walkers = []
  
          for actor_with_transform in self.actors_with_transforms:
              actor = actor_with_transform[0]
              if 'vehicle' in actor.type_id:
                  vehicles.append(actor_with_transform)
              elif 'traffic_light' in actor.type_id:
                  traffic_lights.append(actor_with_transform)
              elif 'speed_limit' in actor.type_id:
                  speed_limits.append(actor_with_transform)
              elif 'walker' in actor.type_id:
                  walkers.append(actor_with_transform)
  
          info_text = []
          if self.hero_actor is not None and len(vehicles) > 1:
              location = self.hero_transform.location
              vehicle_list = [x[0] for x in vehicles if x[0].id != self.hero_actor.id]
  
              def distance(v): return location.distance(v.get_location())
              for n, vehicle in enumerate(sorted(vehicle_list, key=distance)):
                  if n > 15:
                      break
                  vehicle_type = get_actor_display_name(vehicle, truncate=22)
                  info_text.append('% 5d %s' % (vehicle.id, vehicle_type))
          module_manager.get_module(MODULE_HUD).add_info(
              'NEARBY VEHICLES',
              info_text)
  
          return (vehicles, traffic_lights, speed_limits, walkers)
  
      def _render_traffic_lights(self, surface, list_tl, world_to_pixel):
          self.affected_traffic_light = None
  
          for tl in list_tl:
              world_pos = tl.get_location()
              pos = world_to_pixel(world_pos)
  
              if self.args.show_triggers:
                  corners = Util.get_bounding_box(tl)
                  corners = [world_to_pixel(p) for p in corners]
                  pygame.draw.lines(surface, COLOR_BUTTER_1, True, corners, 2)
  
              if self.hero_actor is not None:
                  corners = Util.get_bounding_box(tl)
                  corners = [world_to_pixel(p) for p in corners]
                  tl_t = tl.get_transform()
  
                  transformed_tv = tl_t.transform(tl.trigger_volume.location)
                  hero_location = self.hero_actor.get_location()
                  d = hero_location.distance(transformed_tv)
                  s = Util.length(tl.trigger_volume.extent) + Util.length(self.hero_actor.bounding_box.extent)
                  if (d <= s):
                      # Highlight traffic light
                      self.affected_traffic_light = tl
                      srf = self.traffic_light_surfaces.surfaces['h']
                      surface.blit(srf, srf.get_rect(center=pos))
  
              srf = self.traffic_light_surfaces.surfaces[tl.state]
              surface.blit(srf, srf.get_rect(center=pos))
  
      def _render_speed_limits(self, surface, list_sl, world_to_pixel, world_to_pixel_width):
  
          font_size = world_to_pixel_width(2)
          radius = world_to_pixel_width(2)
          font = pygame.font.SysFont('Arial', font_size)
  
          for sl in list_sl:
  
              x, y = world_to_pixel(sl.get_location())
  
              # Render speed limit
              white_circle_radius = int(radius * 0.75)
  
              pygame.draw.circle(surface, COLOR_SCARLET_RED_1, (x, y), radius)
              pygame.draw.circle(surface, COLOR_ALUMINIUM_0, (x, y), white_circle_radius)
  
              limit = sl.type_id.split('.')[2]
              font_surface = font.render(limit, True, COLOR_ALUMINIUM_5)
  
              if self.args.show_triggers:
                  corners = Util.get_bounding_box(sl)
                  corners = [world_to_pixel(p) for p in corners]
                  pygame.draw.lines(surface, COLOR_PLUM_2, True, corners, 2)
  
              # Blit
              if self.hero_actor is not None:
                  # Rotate font surface with respect to hero vehicle front
                  angle = -self.hero_transform.rotation.yaw - 90.0
                  font_surface = pygame.transform.rotate(font_surface, angle)
                  offset = font_surface.get_rect(center=(x, y))
                  surface.blit(font_surface, offset)
  
              else:
                  surface.blit(font_surface, (x - radius / 2, y - radius / 2))
  
      def _render_walkers(self, surface, list_w, world_to_pixel):
          for w in list_w:
              color = COLOR_PLUM_0
  
              # Compute bounding box points
              bb = w[0].bounding_box.extent
              corners = [
                  carla.Location(x=-bb.x, y=-bb.y),
                  carla.Location(x=bb.x, y=-bb.y),
                  carla.Location(x=bb.x, y=bb.y),
                  carla.Location(x=-bb.x, y=bb.y)]
  
              w[1].transform(corners)
              corners = [world_to_pixel(p) for p in corners]
              pygame.draw.polygon(surface, color, corners)
  
      def _render_vehicles(self, surface, list_v, world_to_pixel):
  
          for v in list_v:
              color = COLOR_SKY_BLUE_0
              if int(v[0].attributes['number_of_wheels']) == 2:
                  color = COLOR_CHOCOLATE_1
              if v[0].attributes['role_name'] == 'hero':
                  color = COLOR_CHAMELEON_0
              # Compute bounding box points
              bb = v[0].bounding_box.extent
              corners = [carla.Location(x=-bb.x, y=-bb.y),
                         carla.Location(x=bb.x - 0.8, y=-bb.y),
                         carla.Location(x=bb.x, y=0),
                         carla.Location(x=bb.x - 0.8, y=bb.y),
                         carla.Location(x=-bb.x, y=bb.y),
                         carla.Location(x=-bb.x, y=-bb.y)
                         ]
              v[1].transform(corners)
              corners = [world_to_pixel(p) for p in corners]
              pygame.draw.lines(surface, color, False, corners, int(math.ceil(4.0 * self.map_image.scale)))
  
      def render_actors(self, surface, vehicles, traffic_lights, speed_limits, walkers):
          # Static actors
          self._render_traffic_lights(surface, [tl[0] for tl in traffic_lights], self.map_image.world_to_pixel)
          self._render_speed_limits(surface, [sl[0] for sl in speed_limits], self.map_image.world_to_pixel,
                                    self.map_image.world_to_pixel_width)
  
          # Dynamic actors
          self._render_vehicles(surface, vehicles, self.map_image.world_to_pixel)
          self._render_walkers(surface, walkers, self.map_image.world_to_pixel)
  
      def clip_surfaces(self, clipping_rect):
          self.actors_surface.set_clip(clipping_rect)
          self.vehicle_id_surface.set_clip(clipping_rect)
          self.result_surface.set_clip(clipping_rect)
  
      def _compute_scale(self, scale_factor):
          m = self.module_input.mouse_pos
  
          # Percentage of surface where mouse position is actually
          px = (m[0] - self.scale_offset[0]) / float(self.prev_scaled_size)
          py = (m[1] - self.scale_offset[1]) / float(self.prev_scaled_size)
  
          # Offset will be the previously accumulated offset added with the
          # difference of mouse positions in the old and new scales
          diff_between_scales = ((float(self.prev_scaled_size) * px) - (float(self.scaled_size) * px),
                                 (float(self.prev_scaled_size) * py) - (float(self.scaled_size) * py))
  
          self.scale_offset = (self.scale_offset[0] + diff_between_scales[0],
                               self.scale_offset[1] + diff_between_scales[1])
  
          # Update previous scale
          self.prev_scaled_size = self.scaled_size
  
          # Scale performed
          self.map_image.scale_map(scale_factor)
  
      def render(self, display):
          if self.actors_with_transforms is None:
              return
          self.result_surface.fill(COLOR_BLACK)
          vehicles, traffic_lights, speed_limits, walkers = self._split_actors()
  
          scale_factor = self.module_input.wheel_offset
          self.scaled_size = int(self.map_image.width * scale_factor)
          if self.scaled_size != self.prev_scaled_size:
              self._compute_scale(scale_factor)
  
          # Render Actors
  
          self.actors_surface.fill(COLOR_BLACK)
          self.render_actors(
              self.actors_surface,
              vehicles,
              traffic_lights,
              speed_limits,
              walkers)
  
          # Render Ids
          self.module_hud.render_vehicles_ids(self.vehicle_id_surface, vehicles,
                                              self.map_image.world_to_pixel, self.hero_actor, self.hero_transform)
  
          # Blit surfaces
          surfaces = ((self.map_image.surface, (0, 0)),
                      (self.actors_surface, (0, 0)),
                      (self.vehicle_id_surface, (0, 0)),
                      )
  
          angle = 0.0 if self.hero_actor is None else self.hero_transform.rotation.yaw + 90.0
          self.traffic_light_surfaces.rotozoom(-angle, self.map_image.scale)
  
          center_offset = (0, 0)
          if self.hero_actor is not None:
  
              hero_location_screen = self.map_image.world_to_pixel(self.hero_transform.location)
              hero_front = self.hero_transform.get_forward_vector()
              translation_offset = (
                  hero_location_screen[0] -
                  self.hero_surface.get_width() /
                  2 +
                  hero_front.x *
                  PIXELS_AHEAD_VEHICLE,
                  (hero_location_screen[1] -
                   self.hero_surface.get_height() /
                   2 +
                   hero_front.y *
                   PIXELS_AHEAD_VEHICLE))
  
              # Apply clipping rect
              clipping_rect = pygame.Rect(translation_offset[0],
                                          translation_offset[1],
                                          self.hero_surface.get_width(),
                                          self.hero_surface.get_height())
              self.clip_surfaces(clipping_rect)
  
              Util.blits(self.result_surface, surfaces)
  
              self.border_round_surface.set_clip(clipping_rect)
  
              self.hero_surface.fill(COLOR_ALUMINIUM_4)
              self.hero_surface.blit(self.result_surface, (-translation_offset[0],
                                                           -translation_offset[1]))
  
              rotated_result_surface = pygame.transform.rotozoom(self.hero_surface, angle, 0.9).convert()
  
              center = (display.get_width() / 2, display.get_height() / 2)
              rotation_pivot = rotated_result_surface.get_rect(center=center)
              display.blit(rotated_result_surface, rotation_pivot)
  
              display.blit(self.border_round_surface, (0, 0))
          else:
              # Translation offset
              translation_offset = (self.module_input.mouse_offset[0] * scale_factor + self.scale_offset[0],
                                    self.module_input.mouse_offset[1] * scale_factor + self.scale_offset[1])
              center_offset = (abs(display.get_width() - self.surface_size) / 2 * scale_factor, 0)
  
              # Apply clipping rect
              clipping_rect = pygame.Rect(-translation_offset[0] - center_offset[0], -translation_offset[1],
                                          self.module_hud.dim[0], self.module_hud.dim[1])
              self.clip_surfaces(clipping_rect)
              Util.blits(self.result_surface, surfaces)
  
              display.blit(self.result_surface, (translation_offset[0] + center_offset[0],
                                                 translation_offset[1]))
  
      def destroy(self):
          if self.spawned_hero is not None:
              self.spawned_hero.destroy()
  # ==============================================================================
  # -- Input -----------------------------------------------------------
  # ==============================================================================
  
  
  class ModuleInput(object):
      def __init__(self, name):
          self.name = name
          self.mouse_pos = (0, 0)
          self.mouse_offset = [0.0, 0.0]
          self.wheel_offset = 0.1
          self.wheel_amount = 0.025
          self._steer_cache = 0.0
          self.control = None
          self._autopilot_enabled = False
  
      def start(self):
          hud = module_manager.get_module(MODULE_HUD)
          hud.notification("Press 'H' or '?' for help.", seconds=4.0)
  
      def render(self, display):
          pass
  
      def tick(self, clock):
          self.parse_input(clock)
  
      def _parse_events(self):
          self.mouse_pos = pygame.mouse.get_pos()
          for event in pygame.event.get():
              if event.type == pygame.QUIT:
                  exit_game()
              elif event.type == pygame.KEYUP:
                  if self._is_quit_shortcut(event.key):
                      exit_game()
                  elif event.key == K_h or (event.key == K_SLASH and pygame.key.get_mods() & KMOD_SHIFT):
                      module_hud = module_manager.get_module(MODULE_HUD)
                      module_hud.help.toggle()
                  elif event.key == K_TAB:
                      module_world = module_manager.get_module(MODULE_WORLD)
                      module_hud = module_manager.get_module(MODULE_HUD)
                      if module_world.hero_actor is None:
                          module_world.select_hero_actor()
                          self.wheel_offset = HERO_DEFAULT_SCALE
                          self.control = carla.VehicleControl()
                          module_hud.notification('Hero Mode')
                      else:
                          self.wheel_offset = MAP_DEFAULT_SCALE
                          self.mouse_offset = [0, 0]
                          self.mouse_pos = [0, 0]
                          module_world.scale_offset = [0, 0]
                          module_world.hero_actor = None
                          module_hud.notification('Map Mode')
                  elif event.key == K_F1:
                      module_hud = module_manager.get_module(MODULE_HUD)
                      module_hud.show_info = not module_hud.show_info
                  elif event.key == K_i:
                      module_hud = module_manager.get_module(MODULE_HUD)
                      module_hud.show_actor_ids = not module_hud.show_actor_ids
                  elif isinstance(self.control, carla.VehicleControl):
                      if event.key == K_q:
                          self.control.gear = 1 if self.control.reverse else -1
                      elif event.key == K_m:
                          self.control.manual_gear_shift = not self.control.manual_gear_shift
                          world = module_manager.get_module(MODULE_WORLD)
                          self.control.gear = world.hero_actor.get_control().gear
                          module_hud = module_manager.get_module(MODULE_HUD)
                          module_hud.notification('%s Transmission' % (
                              'Manual' if self.control.manual_gear_shift else 'Automatic'))
                      elif self.control.manual_gear_shift and event.key == K_COMMA:
                          self.control.gear = max(-1, self.control.gear - 1)
                      elif self.control.manual_gear_shift and event.key == K_PERIOD:
                          self.control.gear = self.control.gear + 1
                      elif event.key == K_p:
                          world = module_manager.get_module(MODULE_WORLD)
                          if world.hero_actor is not None:
                              self._autopilot_enabled = not self._autopilot_enabled
                              world.hero_actor.set_autopilot(self._autopilot_enabled)
                              module_hud = module_manager.get_module(MODULE_HUD)
                              module_hud.notification('Autopilot %s' % ('On' if self._autopilot_enabled else 'Off'))
              elif event.type == pygame.MOUSEBUTTONDOWN:
                  if event.button == 4:
                      self.wheel_offset += self.wheel_amount
                      if self.wheel_offset >= 1.0:
                          self.wheel_offset = 1.0
                  elif event.button == 5:
                      self.wheel_offset -= self.wheel_amount
                      if self.wheel_offset <= 0.1:
                          self.wheel_offset = 0.1
  
      def _parse_keys(self, milliseconds):
          keys = pygame.key.get_pressed()
          self.control.throttle = 1.0 if keys[K_UP] or keys[K_w] else 0.0
          steer_increment = 5e-4 * milliseconds
          if keys[K_LEFT] or keys[K_a]:
              self._steer_cache -= steer_increment
          elif keys[K_RIGHT] or keys[K_d]:
              self._steer_cache += steer_increment
          else:
              self._steer_cache = 0.0
          self._steer_cache = min(0.7, max(-0.7, self._steer_cache))
          self.control.steer = round(self._steer_cache, 1)
          self.control.brake = 1.0 if keys[K_DOWN] or keys[K_s] else 0.0
          self.control.hand_brake = keys[K_SPACE]
  
      def _parse_mouse(self):
          if pygame.mouse.get_pressed()[0]:
              x, y = pygame.mouse.get_pos()
              self.mouse_offset[0] += (1.0 / self.wheel_offset) * (x - self.mouse_pos[0])
              self.mouse_offset[1] += (1.0 / self.wheel_offset) * (y - self.mouse_pos[1])
              self.mouse_pos = (x, y)
  
      def parse_input(self, clock):
          self._parse_events()
          self._parse_mouse()
          if not self._autopilot_enabled:
              if isinstance(self.control, carla.VehicleControl):
                  self._parse_keys(clock.get_time())
                  self.control.reverse = self.control.gear < 0
              world = module_manager.get_module(MODULE_WORLD)
              if (world.hero_actor is not None):
                  world.hero_actor.apply_control(self.control)
  
      @staticmethod
      def _is_quit_shortcut(key):
          return (key == K_ESCAPE) or (key == K_q and pygame.key.get_mods() & KMOD_CTRL)
  
  
  # ==============================================================================
  # -- Global Objects ------------------------------------------------------------
  # ==============================================================================
  module_manager = ModuleManager()
  
  
  # ==============================================================================
  # -- Game Loop ---------------------------------------------------------------
  # ==============================================================================
  
  
  def game_loop(args):
      try:
          # Init Pygame
          pygame.init()
          display = pygame.display.set_mode(
              (args.width, args.height),
              pygame.HWSURFACE | pygame.DOUBLEBUF)
          pygame.display.set_caption(args.description)
  
          font = pygame.font.Font(pygame.font.get_default_font(), 20)
          text_surface = font.render('Rendering map...', True, COLOR_WHITE)
          display.blit(text_surface, text_surface.get_rect(center=(args.width / 2, args.height / 2)))
          pygame.display.flip()
  
          # Init modules
          input_module = ModuleInput(MODULE_INPUT)
          hud_module = ModuleHUD(MODULE_HUD, args.width, args.height)
          world_module = ModuleWorld(MODULE_WORLD, args, timeout=2.0)
  
          # Register Modules
          module_manager.register_module(world_module)
          module_manager.register_module(hud_module)
          module_manager.register_module(input_module)
  
          module_manager.start_modules()
  
          clock = pygame.time.Clock()
          while True:
              clock.tick_busy_loop(60)
  
              module_manager.tick(clock)
              module_manager.render(display)
  
              pygame.display.flip()
  
      except KeyboardInterrupt:
          print('\nCancelled by user. Bye!')
  
      finally:
          if world_module is not None:
              world_module.destroy()
  
  def exit_game():
      module_manager.clear_modules()
      pygame.quit()
      sys.exit()
  
  # ==============================================================================
  # -- Main --------------------------------------------------------------------
  # ==============================================================================
  
  
  def main():
      # Parse arguments
      argparser = argparse.ArgumentParser(
          description='CARLA No Rendering Mode Visualizer')
      argparser.add_argument(
          '-v', '--verbose',
          action='store_true',
          dest='debug',
          help='print debug information')
      argparser.add_argument(
          '--host',
          metavar='H',
          default='127.0.0.1',
          help='IP of the host server (default: 127.0.0.1)')
      argparser.add_argument(
          '-p', '--port',
          metavar='P',
          default=2000,
          type=int,
          help='TCP port to listen to (default: 2000)')
      argparser.add_argument(
          '--res',
          metavar='WIDTHxHEIGHT',
          default='1280x720',
          help='window resolution (default: 1280x720)')
      argparser.add_argument(
          '--filter',
          metavar='PATTERN',
          default='vehicle.*',
          help='actor filter (default: "vehicle.*")')
      argparser.add_argument(
          '--map',
          metavar='TOWN',
          default=None,
          help='start a new episode at the given TOWN')
      argparser.add_argument(
          '--no-rendering',
          action='store_true',
          help='switch off server rendering')
      argparser.add_argument(
          '--show-triggers',
          action='store_true',
          help='show trigger boxes of traffic signs')
      argparser.add_argument(
          '--show-connections',
          action='store_true',
          help='show waypoint connections')
      argparser.add_argument(
          '--show-spawn-points',
          action='store_true',
          help='show recommended spawn points')
  
      args = argparser.parse_args()
      args.description = argparser.description
      args.width, args.height = [int(x) for x in args.res.split('x')]
  
      log_level = logging.DEBUG if args.debug else logging.INFO
      logging.basicConfig(format='%(levelname)s: %(message)s', level=log_level)
  
      logging.info('listening to server %s:%s', args.host, args.port)
      print(__doc__)
  
      game_loop(args)
  
  if __name__ == '__main__':
      main()
  