diff --git a/.gitignore b/.gitignore
index ab303c9..e121693 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,5 @@
 config.ini
 *.db
 *.log
+/temp/*
+*.pyc
diff --git a/api_app.py b/api_app.py
index f9adff1..de0a777 100644
--- a/api_app.py
+++ b/api_app.py
@@ -85,6 +85,19 @@ def select_frames(conn, n, url_params):
     cur.execute(sql)
     rows = cur.fetchall()
     return rows
+    
+def select_stations(conn, n):
+    """
+    Query rows in the stations table
+    :param conn: the Connection object
+    :return:
+    """
+    cur = conn.cursor()
+    sql = 'SELECT * FROM stations ORDER BY last_heard_unix DESC LIMIT {n}'.format(n=n)
+    print(sql)
+    cur.execute(sql)
+    rows = cur.fetchall()
+    return rows
 
 @api_app.route('/')
 def index():
@@ -113,12 +126,82 @@ def index():
             station['time_ago'] = timeago.format(station['last_heard_unix'], datetime.datetime.now())
 
 
+    # Map stuff
+    frames_locs = list(filter(lambda x: x['latitude'] != None, frames))
+    # Make a GeoJSON
+    geojs = json.dumps({
+     "type": "FeatureCollection",
+     "features":[
+           {
+                "type":"Feature",
+                "geometry": {
+                "type":"Point",
+                "coordinates":[frame['longitude'], frame['latitude']],
+            },
+                "properties":frame,
+        
+         } for frame in frames_locs
+    ]  
+    })
+
     return render_template('index.html',
                             station_call = config['Settings']['station_call'],
                             station_lat = config['Settings']['station_lat'],
                             station_lon = config['Settings']['station_lon'],
                             frames = frames,
-                            stations = stations)
+                            stations = stations,
+                            geojs = geojs)
+
+@api_app.route('/map')
+def map():
+
+    # Get the default list of frames from the API
+    frames = json.loads(requests.get(config['Settings']['base_url']+"/packets").text)['data']
+
+    frames_locs = list(filter(lambda x: x['latitude'] != None, frames))
+
+    # Make a GeoJSON
+    geojs = json.dumps({
+     "type": "FeatureCollection",
+     "features":[
+           {
+                "type":"Feature",
+                "geometry": {
+                "type":"Point",
+                "coordinates":[frame['longitude'], frame['latitude']],
+            },
+                "properties":frame,
+        
+         } for frame in frames_locs
+    ]  
+    })
+
+    # Make markers for all the frames
+    # id_counter = 0
+    # markers = ''
+    # marker_ids = []
+    # for frame in frames:
+    #     if frame['latitude'] != None:
+    #         # Create unique ID for each marker
+    #         idd = 'frame' + str(id_counter)
+    #         id_counter += 1
+
+    #         # Create each marker
+    #         markers += "var {idd} = L.marker([{latitude}, {longitude}]);\
+    #                     {idd}.addTo(map).bindTooltip('{from_ssid}', permanent=true).openTooltip();".format(idd=idd, latitude=frame['latitude'],\
+    #                                                                                 longitude=frame['longitude'],
+    #                                                                                 from_ssid=frame['from'],
+    #                                                                                 created=frame['created'])
+    #         # Try to make a list of markers for Leaflet, but not working
+    #         marker_ids.append(idd)
+
+
+    return render_template('map.html',
+                           station_lat = config['Settings']['station_lat'],
+                           station_lon = config['Settings']['station_lon'],
+                           station_call = config['Settings']['station_call'],
+                           #markers = markers,
+                           geojs = geojs)
 
 class Packets(Resource):
     def get(self):
@@ -145,7 +228,7 @@ class Stations(Resource):
 
         conn = get_db_connection()
         # Limit to number of records requested
-        data = select_all_stations(conn)
+        data = select_stations(conn, n = n)
         # Sort by created date, descending (https://stackoverflow.com/a/45266808)
         #data.sort(key=operator.itemgetter('created'), reverse=True)
         return {'data':data}, 200  # return data and 200 OK code
diff --git a/aprs_tool.code-workspace b/aprs_tool.code-workspace
new file mode 100644
index 0000000..362d7c2
--- /dev/null
+++ b/aprs_tool.code-workspace
@@ -0,0 +1,7 @@
+{
+	"folders": [
+		{
+			"path": "."
+		}
+	]
+}
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
index 3b864fd..9b3aad8 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -4,18 +4,79 @@
     <meta charset="UTF-8">
     <title>{{station_call}} Status</title>
 
+  <!-- Leaflet's CSS -->
+  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
+  integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
+  crossorigin=""/>
+  <!-- Make sure you put this AFTER Leaflet's CSS -->
+  <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
+  integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
+  crossorigin=""></script>
+
     <style>
       table, th, td {
         border: 1px solid black;
       }
+      #map { height: 250px; }
+      .leaflet-tooltip.my-labels {
+        background-color: transparent;
+        border: transparent;
+        box-shadow: none;
+        }
     </style>
 </head>
 <body>
-<h1>{{station_call}} Status</h1>
-Station location: {{station_lat}}, {{station_lon}}
+<div style="width: 100%; overflow: hidden;">
+    <div style="width: 50%; float: left;">
 
-<h2> About </h2>
-This is a work in progress. See <a href="https://amiok.net/gitea/W1CDN/aprs_tool">https://amiok.net/gitea/W1CDN/aprs_tool</a> for usage.
+      <h1>{{station_call}} Status</h1>
+      Station location: {{station_lat}}, {{station_lon}}
+
+      <h2> About </h2>
+      This is a work in progress. See <a href="https://amiok.net/gitea/W1CDN/aprs_tool">https://amiok.net/gitea/W1CDN/aprs_tool</a> for usage.
+    </div>
+    <div style="margin-left: 50%;">
+      <div id="map"></div>
+        <script>
+            var map = L.map('map').setView([{{station_lat}}, {{station_lon}}], 10);
+            L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'}).addTo(map);
+
+            //{{markers|safe}}
+
+            // Show station location
+            var station = L.marker([{{station_lat}}, {{station_lon}}]).addTo(map).bindTooltip('{{station_call}}', {permanent: true}).openTooltip();
+            
+            // Show GeoJSON of markers
+            var group = L.geoJSON({{geojs|safe}},
+                    {
+                        style: function (feature) {
+                            return {color: feature.properties.color};
+                        }
+                    });
+
+            // group.bindTooltip(function (layer) {
+            //                           return 'Object '+layer.feature.properties.object_name+' from '+layer.feature.properties.from;
+            //                           }, {permanent: false}).openTooltip().addTo(map);
+            // Hacked together from https://gis.stackexchange.com/a/246919
+            var pointLayer = L.geoJSON(null, {
+                                pointToLayer: function(feature,latlng){
+                                  //(true condition) ? "true" : "false"
+                                  label = (feature.properties.object_name === null) ? String(feature.properties.from) : String(feature.properties.object_name)
+                                  //label = String('Object '+feature.properties.object_name+' from '+feature.properties.from) // Must convert to string, .bindTooltip can't use straight 'feature.properties.attribute'
+                                  return new L.CircleMarker(latlng, {
+                                    radius: 1,
+                                  }).bindTooltip(label, {permanent: true, opacity: 0.7, className: "my-labels"}).openTooltip();
+                                  }
+                                });
+            pointLayer.addData({{geojs|safe}});
+            map.addLayer(pointLayer);
+            
+
+            // Zoom to show all
+            map.fitBounds(group.getBounds().pad(0.3));
+        </script>
+    </div>
+</div>
 
 <h2> Recent RF Packets </h2>
    <table>
@@ -40,6 +101,8 @@ This is a work in progress. See <a href="https://amiok.net/gitea/W1CDN/aprs_tool
       {% endfor %}
   </table>
 
+  
+
 <h2> Recent Stations </h2>
 <table>
   <tr>
diff --git a/templates/map.html b/templates/map.html
new file mode 100644
index 0000000..614e608
--- /dev/null
+++ b/templates/map.html
@@ -0,0 +1,42 @@
+<html>
+<head>
+    <!-- Leaflet's CSS -->
+    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
+     integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
+     crossorigin=""/>
+     <!-- Make sure you put this AFTER Leaflet's CSS -->
+    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
+    integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
+    crossorigin=""></script>
+    <style>
+        #map { height: 100%; }
+    </style>
+</head>
+<body>
+    <div id="map"></div>
+
+    <script>
+        var map = L.map('map').setView([{{station_lat}}, {{station_lon}}], 10);
+        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'}).addTo(map);
+
+        //{{markers|safe}}
+
+        // Show station location
+        var station = L.marker([{{station_lat}}, {{station_lon}}]).addTo(map).bindTooltip('{{station_call}}', permanent=true).openTooltip();
+        
+        // Show GeoJSON of markers
+        var group = L.geoJSON({{geojs|safe}},
+                {
+                    style: function (feature) {
+                        return {color: feature.properties.color};
+                    }
+                }).bindTooltip(function (layer) {
+            return 'Object '+layer.feature.properties.object_name+' from '+layer.feature.properties.from;
+            }, permanent=true).addTo(map);
+
+        // Zoom to show all
+        map.fitBounds(group.getBounds().pad(0.2));
+
+    </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/test_db.py b/test_db.py
deleted file mode 100644
index e0959e5..0000000
--- a/test_db.py
+++ /dev/null
@@ -1,36 +0,0 @@
-
-# Learn how to update database
-
-import sqlite3
-
-def get_db_connection():
-    conn = sqlite3.connect('database.db')
-    conn.row_factory = sqlite3.Row
-    return conn
-
-conn = get_db_connection()
-
-# Grab a random row from frames table and pretend it is new
-cur = conn.cursor()
-cur.execute("SELECT [from], id, created_unix FROM frames ORDER BY RANDOM() LIMIT 1;")
-rows = cur.fetchall()
-results = dict(rows[0])
-values = ', '.join('"%s"' % w for w in results.values())
-
-
-# Build query
-# "from" is wrappedin [] because it is a reserved word and using '' doesn't work.
-query3 = "INSERT INTO stations ([from], frames_id, last_heard_unix, count) \
-VALUES("+values+", 1) \
-ON CONFLICT([from]) \
-DO UPDATE SET count = count + 1;"
-
-# example https://stackoverflow.com/a/50718957/2152245
-# query2 = "INSERT INTO stations ([from], frames_id, last_heard_unix, count) \
-# VALUES('KC9TZN-8', 4068, 1687623864, 1) \
-# ON CONFLICT([from]) \
-# DO UPDATE SET count = count + 1;"
-
-conn.execute(query3)
-conn.commit()
-conn.close()