Initial HTTP support with planes animated using google map.
This commit is contained in:
parent
4203b9caf6
commit
a583615b66
16
README.md
16
README.md
|
@ -16,7 +16,10 @@ The main features are:
|
|||
* Interactive mode where aircrafts currently detected are shown
|
||||
as a list refreshing as more data arrives.
|
||||
* CPR coordinates decoding and track calculation from velocity.
|
||||
* TCP server streaming and recceiving raw data to/from connected clients (using --net).
|
||||
* TCP server streaming and recceiving raw data to/from connected clients
|
||||
(using --net).
|
||||
* Embedded HTTP server that displays the currently detected aircrafts on
|
||||
Google Map.
|
||||
|
||||
Installation
|
||||
---
|
||||
|
@ -39,6 +42,11 @@ To run the program in interactive mode:
|
|||
|
||||
./dump1090 --interactive
|
||||
|
||||
To run the program in interactive mode, with networking support, and connect
|
||||
with your browser to http://localhost:8080 to see live traffic:
|
||||
|
||||
./dump1090 --interactive --net
|
||||
|
||||
In iteractive mode it is possible to have a less information dense but more
|
||||
"arcade style" output, where the screen is refreshed every second displaying
|
||||
all the recently seen aircrafts with some additional information such as
|
||||
|
@ -136,7 +144,11 @@ normal traffic from RTL devices or from file when --ifile is used.
|
|||
It is possible to use Dump1090 just as an hub using --ifile with /dev/zero
|
||||
as argument as in the following example:
|
||||
|
||||
./dump1090 --ifile /dev/zero --net --interactive
|
||||
./dump1090 --net-only
|
||||
|
||||
Or alternatively to see what's happening on the screen:
|
||||
|
||||
./dump1090 --net-only --interactive
|
||||
|
||||
Then you can feed it from different data sources from the internet.
|
||||
|
||||
|
|
302
dump1090.c
302
dump1090.c
|
@ -40,6 +40,7 @@
|
|||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
#include "rtl-sdr.h"
|
||||
#include "anet.h"
|
||||
|
||||
|
@ -81,7 +82,8 @@
|
|||
#define MODES_NET_MAX_FD 1024
|
||||
#define MODES_NET_OUTPUT_RAW_PORT 30002
|
||||
#define MODES_NET_INPUT_RAW_PORT 30001
|
||||
#define MODES_CLIENT_BUF_SIZE 256
|
||||
#define MODES_NET_HTTP_PORT 8080
|
||||
#define MODES_CLIENT_BUF_SIZE 1024
|
||||
|
||||
#define MODES_NOTUSED(V) ((void) V)
|
||||
|
||||
|
@ -142,6 +144,7 @@ struct {
|
|||
int maxfd; /* Greatest fd currently active. */
|
||||
int ros; /* Raw output listening socket. */
|
||||
int ris; /* Raw input listening socket. */
|
||||
int https; /* HTTP listening socket. */
|
||||
|
||||
/* Configuration */
|
||||
char *filename; /* Input form file, --ifile option. */
|
||||
|
@ -150,8 +153,10 @@ struct {
|
|||
int raw; /* Raw output format. */
|
||||
int debug; /* Debugging mode. */
|
||||
int net; /* Enable networking. */
|
||||
int net_only; /* Enable just networking. */
|
||||
int net_output_raw_port; /* Raw output TCP port. */
|
||||
int net_input_raw_port; /* Raw input TCP port. */
|
||||
int net_http_port; /* HTTP port. */
|
||||
int interactive; /* Interactive mode */
|
||||
int interactive_rows; /* Interactive mode: max number of rows. */
|
||||
int interactive_ttl; /* Interactive mode: TTL before deletion. */
|
||||
|
@ -169,6 +174,7 @@ struct {
|
|||
long long stat_goodcrc;
|
||||
long long stat_badcrc;
|
||||
long long stat_fixed;
|
||||
long long stat_http_requests;
|
||||
} Modes;
|
||||
|
||||
/* The struct we use to store information about a decoded message. */
|
||||
|
@ -244,8 +250,10 @@ void modesInitConfig(void) {
|
|||
Modes.check_crc = 1;
|
||||
Modes.raw = 0;
|
||||
Modes.net = 0;
|
||||
Modes.net_only = 0;
|
||||
Modes.net_output_raw_port = MODES_NET_OUTPUT_RAW_PORT;
|
||||
Modes.net_input_raw_port = MODES_NET_INPUT_RAW_PORT;
|
||||
Modes.net_http_port = MODES_NET_HTTP_PORT;
|
||||
Modes.onlyaddr = 0;
|
||||
Modes.debug = 0;
|
||||
Modes.interactive = 0;
|
||||
|
@ -292,6 +300,7 @@ void modesInit(void) {
|
|||
Modes.stat_goodcrc = 0;
|
||||
Modes.stat_badcrc = 0;
|
||||
Modes.stat_fixed = 0;
|
||||
Modes.stat_http_requests = 0;
|
||||
Modes.exit = 0;
|
||||
}
|
||||
|
||||
|
@ -1257,9 +1266,13 @@ void detectModeS(uint16_t *m, uint32_t mlen) {
|
|||
* further processing and visualization. */
|
||||
void useModesMessage(struct modesMessage *mm) {
|
||||
if (!Modes.stats && (Modes.check_crc == 0 || mm->crcok)) {
|
||||
if (Modes.interactive) {
|
||||
/* Track aircrafts in interactive mode or if the HTTP
|
||||
* interface is enabled. */
|
||||
if (Modes.interactive || Modes.stat_http_requests > 0) {
|
||||
interactiveReceiveData(mm);
|
||||
} else {
|
||||
}
|
||||
/* In non-interactive way, display messages on standard output. */
|
||||
if (!Modes.interactive) {
|
||||
displayModesMessage(mm);
|
||||
if (!Modes.raw && !Modes.onlyaddr) printf("\n");
|
||||
}
|
||||
|
@ -1596,27 +1609,31 @@ void snipMode(int level) {
|
|||
|
||||
/* Networking "stack" initialization. */
|
||||
void modesInitNet(void) {
|
||||
struct {
|
||||
char *descr;
|
||||
int *socket;
|
||||
int port;
|
||||
} services[3] = {
|
||||
{"Raw TCP output", &Modes.ros, Modes.net_output_raw_port},
|
||||
{"Raw TCP input", &Modes.ris, Modes.net_input_raw_port},
|
||||
{"HTTP server", &Modes.https, Modes.net_http_port}
|
||||
};
|
||||
int j;
|
||||
|
||||
memset(Modes.clients,0,sizeof(Modes.clients));
|
||||
Modes.maxfd = -1;
|
||||
|
||||
/* Raw output port */
|
||||
Modes.ros = anetTcpServer(Modes.aneterr, Modes.net_output_raw_port, NULL);
|
||||
if (Modes.ros == -1) {
|
||||
fprintf(stderr, "Error opening raw TCP output port %d: %s\n",
|
||||
Modes.net_output_raw_port, Modes.aneterr);
|
||||
for (j = 0; j < 3; j++) {
|
||||
int s = anetTcpServer(Modes.aneterr, services[j].port, NULL);
|
||||
if (s == -1) {
|
||||
fprintf(stderr, "Error opening the listening port %d (%s): %s\n",
|
||||
services[j].port, services[j].descr, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Raw input port */
|
||||
Modes.ris = anetTcpServer(Modes.aneterr, Modes.net_input_raw_port, NULL);
|
||||
if (Modes.ris == -1) {
|
||||
fprintf(stderr, "Error opening raw TCP input port %d: %s\n",
|
||||
Modes.net_input_raw_port, Modes.aneterr);
|
||||
exit(1);
|
||||
anetNonBlock(Modes.aneterr, s);
|
||||
*services[j].socket = s;
|
||||
}
|
||||
|
||||
anetNonBlock(Modes.aneterr, Modes.ros);
|
||||
anetNonBlock(Modes.aneterr, Modes.ris);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
|
||||
|
@ -1627,10 +1644,11 @@ void modesAcceptClients(void) {
|
|||
int fd, port;
|
||||
unsigned int j;
|
||||
struct client *c;
|
||||
int services[2];
|
||||
int services[3];
|
||||
|
||||
services[0] = Modes.ros;
|
||||
services[1] = Modes.ris;
|
||||
services[2] = Modes.https;
|
||||
|
||||
for (j = 0; j < sizeof(services)/sizeof(int); j++) {
|
||||
fd = anetTcpAccept(Modes.aneterr, services[j], NULL, &port);
|
||||
|
@ -1712,16 +1730,30 @@ int hexDigitVal(int c) {
|
|||
|
||||
/* This function decodes a string representing a Mode S message in
|
||||
* raw hex format like: *8D4B969699155600E87406F5B69F;
|
||||
* The string is supposed to be at the start of the client buffer
|
||||
* and null-terminated.
|
||||
*
|
||||
* The message is passed to the higher level layers, so it feeds
|
||||
* the selected screen output, the network output and so forth.
|
||||
*
|
||||
* If the message looks invalid is silently discarded. */
|
||||
void decodeHexMessage(char *hex) {
|
||||
void decodeHexMessage(struct client *c) {
|
||||
char *hex = c->buf;
|
||||
int l = strlen(hex), j;
|
||||
unsigned char msg[MODES_LONG_MSG_BYTES];
|
||||
struct modesMessage mm;
|
||||
|
||||
/* Remove spaces on the left and on the right. */
|
||||
while(l && isspace(hex[l-1])) {
|
||||
hex[l-1] = '\0';
|
||||
l--;
|
||||
}
|
||||
while(isspace(*hex)) {
|
||||
hex++;
|
||||
l--;
|
||||
}
|
||||
|
||||
/* Turn the message into binary. */
|
||||
if (l < 2 || hex[0] != '*' || hex[l-1] != ';') return;
|
||||
hex++; l-=2; /* Skip * and ; */
|
||||
if (l > MODES_LONG_MSG_BYTES*2) return; /* Too long message... broken. */
|
||||
|
@ -1736,52 +1768,164 @@ void decodeHexMessage(char *hex) {
|
|||
useModesMessage(&mm);
|
||||
}
|
||||
|
||||
/* This function polls all the clients using read() in order to receive new
|
||||
/* Return a description of planes in json. */
|
||||
char *aircraftsToJson(int *len) {
|
||||
struct aircraft *a = Modes.aircrafts;
|
||||
int buflen = 1024; /* The initial buffer is incremented as needed. */
|
||||
char *buf = malloc(buflen), *p = buf;
|
||||
int l;
|
||||
|
||||
l = snprintf(p,buflen,"[\n");
|
||||
p += l; buflen -= l;
|
||||
while(a) {
|
||||
int altitude = a->altitude, speed = a->speed;
|
||||
|
||||
/* Convert units to metric if --metric was specified. */
|
||||
if (Modes.metric) {
|
||||
altitude /= 3.2828;
|
||||
speed *= 1.852;
|
||||
}
|
||||
|
||||
if (a->lat != 0 && a->lon != 0) {
|
||||
l = snprintf(p,buflen,
|
||||
"{\"hex\":\"%s\", \"lat\":%f, \"lon\":%f, \"track\":%d},\n",
|
||||
a->hexaddr, a->lat, a->lon, a->track);
|
||||
p += l; buflen -= l;
|
||||
/* Resize if needed. */
|
||||
if (buflen < 256) {
|
||||
int used = p-buf;
|
||||
buflen += 1024; /* Our increment. */
|
||||
buf = realloc(buf,used+buflen);
|
||||
p = buf+used;
|
||||
}
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
/* Remove the final comma if any, and closes the json array. */
|
||||
if (*(p-2) == ',') {
|
||||
*(p-2) = '\n';
|
||||
p--;
|
||||
buflen++;
|
||||
}
|
||||
l = snprintf(p,buflen,"]\n");
|
||||
p += l; buflen -= l;
|
||||
|
||||
*len = p-buf;
|
||||
return buf;
|
||||
}
|
||||
|
||||
#define MODES_CONTENT_TYPE_HTML "text/html;charset=utf-8"
|
||||
#define MODES_CONTENT_TYPE_JSON "application/json;charset=utf-8"
|
||||
|
||||
/* Get an HTTP request header and write the response to the client.
|
||||
* Again here we assume that the socket buffer is enough without doing
|
||||
* any kind of userspace buffering. */
|
||||
void handleHTTPRequest(struct client *c) {
|
||||
char hdr[512];
|
||||
int clen, hdrlen;
|
||||
int keepalive;
|
||||
char *p, *url, *content;
|
||||
char *ctype;
|
||||
|
||||
/* printf("HTTP request: %s\n", c->buf); */
|
||||
|
||||
/* Minimally parse the request. */
|
||||
keepalive = strstr(c->buf, "keep-alive") != NULL;
|
||||
p = strchr(c->buf,' ');
|
||||
if (!p) return; /* There should be the method and a space... */
|
||||
url = ++p; /* Now this should point to the requested URL. */
|
||||
p = strchr(p, ' ');
|
||||
if (!p) return; /* There should be a space before HTTP/... */
|
||||
*p = '\0';
|
||||
/* printf("URL: %s\n", url); */
|
||||
|
||||
/* Select the content to send, we have just two so far:
|
||||
* "/" -> Our google map application.
|
||||
* "/data.json" -> Our ajax request to update planes. */
|
||||
if (strstr(url, "/data.json")) {
|
||||
content = aircraftsToJson(&clen);
|
||||
ctype = MODES_CONTENT_TYPE_JSON;
|
||||
} else {
|
||||
struct stat sbuf;
|
||||
int fd = -1;
|
||||
|
||||
if (stat("gmap.html",&sbuf) != -1 &&
|
||||
(fd = open("gmap.html",O_RDONLY)) != -1)
|
||||
{
|
||||
content = malloc(sbuf.st_size);
|
||||
read(fd,content,sbuf.st_size);
|
||||
} else {
|
||||
char buf[128];
|
||||
|
||||
clen = snprintf(buf,sizeof(buf),"Error opening HTML file: %s",
|
||||
strerror(errno));
|
||||
content = strdup(buf);
|
||||
}
|
||||
if (fd != -1) close(fd);
|
||||
ctype = MODES_CONTENT_TYPE_HTML;
|
||||
}
|
||||
|
||||
/* Create the header and send the reply. */
|
||||
hdrlen = snprintf(hdr, sizeof(hdr),
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: Dump1090\r\n"
|
||||
"Content-Type: %s\r\n"
|
||||
"Connection: %s\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"\r\n",
|
||||
ctype,
|
||||
keepalive ? "keep-alive" : "close",
|
||||
clen);
|
||||
write(c->fd, hdr, hdrlen);
|
||||
|
||||
/* Send the actual content. */
|
||||
write(c->fd, content, clen);
|
||||
free(content);
|
||||
Modes.stat_http_requests++;
|
||||
}
|
||||
|
||||
/* This function polls the clients using read() in order to receive new
|
||||
* messages from the net.
|
||||
*
|
||||
* Every full message received is decoded and passed to the higher layers. */
|
||||
void modesReceiveRawInput(void) {
|
||||
int j;
|
||||
struct client *c;
|
||||
|
||||
for (j = 0; j <= Modes.maxfd; j++) {
|
||||
c = Modes.clients[j];
|
||||
if (c && c->service == Modes.ris) {
|
||||
* The message is supposed to be separated by the next message by the
|
||||
* separator 'sep', that is a null-terminated C string.
|
||||
*
|
||||
* Every full message received is decoded and passed to the higher layers
|
||||
* calling the function 'handler'. */
|
||||
void modesReadFromClient(struct client *c, char *sep,
|
||||
void(*handler)(struct client *))
|
||||
{
|
||||
while(1) {
|
||||
int left = sizeof(c->buf) - c->buflen;
|
||||
int nread = read(j, c->buf+c->buflen, left);
|
||||
int decoded = 0;
|
||||
int oldpos = c->buflen;
|
||||
int nread = read(c->fd, c->buf+c->buflen, left);
|
||||
int fullmsg = 0;
|
||||
int i;
|
||||
char *p;
|
||||
|
||||
if (nread < 0) {
|
||||
if (nread == 0 || errno != EAGAIN) {
|
||||
/* Error, or end of file. */
|
||||
modesFreeClient(j);
|
||||
modesFreeClient(c->fd);
|
||||
}
|
||||
break; /* Serve next client. */
|
||||
}
|
||||
c->buflen += nread;
|
||||
|
||||
/* If there is a complete message there must be a newline
|
||||
* in the buffer. The iteration starts from 'oldpos' as
|
||||
* we need to check only the chars we read in this interaction
|
||||
* as we are sure there is no newline in the pre-existing
|
||||
* buffer. */
|
||||
for (i = oldpos; i < c->buflen; i++) {
|
||||
if (c->buf[i] == '\n') {
|
||||
c->buf[i] = '\0';
|
||||
if (i && c->buf[i-1] == '\r') c->buf[i-1] = '\0';
|
||||
decodeHexMessage(c->buf);
|
||||
/* If there is a complete message there must be the separator 'sep'
|
||||
* in the buffer, note that we full-scan the buffer at every read
|
||||
* for simplicity. */
|
||||
if ((p = strstr(c->buf, sep)) != NULL) {
|
||||
i = p - c->buf; /* Turn it as an index inside the buffer. */
|
||||
c->buf[i] = '\0'; /* Te handler expects null terminated strings. */
|
||||
handler(c); /* Call the function to process the message. */
|
||||
/* Move what's left at the start of the buffer. */
|
||||
i++;
|
||||
i += strlen(sep); /* The separator is part of the previous msg. */
|
||||
memmove(c->buf,c->buf+i,c->buflen-i);
|
||||
c->buflen -= i;
|
||||
/* Maybe there are more messages inside the buffer.
|
||||
* Start looping from the start again. */
|
||||
i = -1;
|
||||
decoded = 1;
|
||||
}
|
||||
fullmsg = 1;
|
||||
}
|
||||
/* If our buffer is full discard it, this is some badly
|
||||
* formatted shit. */
|
||||
|
@ -1792,9 +1936,22 @@ void modesReceiveRawInput(void) {
|
|||
}
|
||||
/* If no message was decoded process the next client, otherwise
|
||||
* read more data from the same client. */
|
||||
if (!decoded) break;
|
||||
if (!fullmsg) break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read data from clients. This function actually delegates a lower-level
|
||||
* function that depends on the kind of service (raw, http, ...). */
|
||||
void modesReadFromClients(void) {
|
||||
int j;
|
||||
struct client *c;
|
||||
|
||||
for (j = 0; j <= Modes.maxfd; j++) {
|
||||
if ((c = Modes.clients[j]) == NULL) continue;
|
||||
if (c->service == Modes.ris)
|
||||
modesReadFromClient(c,"\n",decodeHexMessage);
|
||||
else if (c->service == Modes.https)
|
||||
modesReadFromClient(c,"\r\n\r\n",handleHTTPRequest);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1812,8 +1969,10 @@ void showHelp(void) {
|
|||
"--interactive-ttl <sec> Remove from list if idle for <sec> (default: 60).\n"
|
||||
"--raw Show only messages hex values.\n"
|
||||
"--net Enable networking.\n"
|
||||
"--net-only Enable just networking, no RTL device or file used.\n"
|
||||
"--net-ro-port <port> TCP listening port for raw output (default: 30002).\n"
|
||||
"--net-ri-port <port> TCP listening port for raw input (default: 30001).\n"
|
||||
"--net-http-port <port> HTTP server port (default: 8080).\n"
|
||||
"--no-fix Disable single-bits error correction using CRC.\n"
|
||||
"--no-crc-check Disable messages with broken CRC (discouraged).\n"
|
||||
"--stats With --ifile print stats at exit. No other output.\n"
|
||||
|
@ -1825,6 +1984,26 @@ void showHelp(void) {
|
|||
);
|
||||
}
|
||||
|
||||
/* This function is called a few times every second by main in order to
|
||||
* perform tasks we need to do continuously, like accepting new clients
|
||||
* from the net, refreshing the screen in interactive mode, and so forth. */
|
||||
void backgroundTasks(void) {
|
||||
if (Modes.net) {
|
||||
modesAcceptClients();
|
||||
modesReadFromClients();
|
||||
}
|
||||
|
||||
/* Refresh screen when in interactive mode. */
|
||||
if (Modes.interactive &&
|
||||
(mstime() - Modes.interactive_last_update) >
|
||||
MODES_INTERACTIVE_REFRESH_TIME)
|
||||
{
|
||||
interactiveRemoveStaleAircrafts();
|
||||
interactiveShowData();
|
||||
Modes.interactive_last_update = mstime();
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int j;
|
||||
|
||||
|
@ -1853,10 +2032,15 @@ int main(int argc, char **argv) {
|
|||
Modes.raw = 1;
|
||||
} else if (!strcmp(argv[j],"--net")) {
|
||||
Modes.net = 1;
|
||||
} else if (!strcmp(argv[j],"--net-only")) {
|
||||
Modes.net = 1;
|
||||
Modes.net_only = 1;
|
||||
} else if (!strcmp(argv[j],"--net-ro-port") && more) {
|
||||
Modes.net_output_raw_port = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-ri-port") && more) {
|
||||
Modes.net_input_raw_port = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--net-http-port") && more) {
|
||||
Modes.net_http_port = atoi(argv[++j]);
|
||||
} else if (!strcmp(argv[j],"--onlyaddr")) {
|
||||
Modes.onlyaddr = 1;
|
||||
} else if (!strcmp(argv[j],"--metric")) {
|
||||
|
@ -1888,7 +2072,9 @@ int main(int argc, char **argv) {
|
|||
|
||||
/* Initialization */
|
||||
modesInit();
|
||||
if (Modes.filename == NULL) {
|
||||
if (Modes.net_only) {
|
||||
fprintf(stderr,"Net-only mode, no RTL device or file open.\n");
|
||||
} else if (Modes.filename == NULL) {
|
||||
modesInitRTLSDR();
|
||||
} else {
|
||||
if (Modes.filename[0] == '-' && Modes.filename[1] == '\0') {
|
||||
|
@ -1900,6 +2086,13 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
if (Modes.net) modesInitNet();
|
||||
|
||||
/* If the user specifies --net-only, just run in order to serve network
|
||||
* clients without reading data from the RTL device. */
|
||||
while (Modes.net_only) {
|
||||
backgroundTasks();
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
/* Create the thread that will read the data from the device. */
|
||||
pthread_create(&Modes.reader_thread, NULL, readerThreadEntryPoint, NULL);
|
||||
|
||||
|
@ -1922,20 +2115,7 @@ int main(int argc, char **argv) {
|
|||
* slow processors). */
|
||||
pthread_mutex_unlock(&Modes.data_mutex);
|
||||
detectModeS(Modes.magnitude, Modes.data_len/2);
|
||||
if (Modes.net) {
|
||||
modesAcceptClients();
|
||||
modesReceiveRawInput();
|
||||
}
|
||||
|
||||
/* Refresh screen when in interactive mode. */
|
||||
if (Modes.interactive &&
|
||||
(mstime() - Modes.interactive_last_update) >
|
||||
MODES_INTERACTIVE_REFRESH_TIME)
|
||||
{
|
||||
interactiveRemoveStaleAircrafts();
|
||||
interactiveShowData();
|
||||
Modes.interactive_last_update = mstime();
|
||||
}
|
||||
backgroundTasks();
|
||||
pthread_mutex_lock(&Modes.data_mutex);
|
||||
if (Modes.exit) break;
|
||||
}
|
||||
|
|
88
gmap.html
Normal file
88
gmap.html
Normal file
|
@ -0,0 +1,88 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
||||
<style type="text/css">
|
||||
html { height: 100% }
|
||||
body { height: 100%; margin: 0; padding: 0 }
|
||||
#map_canvas { height: 100% }
|
||||
</style>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js">
|
||||
</script>
|
||||
<script type="text/javascript"
|
||||
src="https://maps.googleapis.com/maps/api/js?sensor=true">
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
Map=null;
|
||||
CenterLat=50.0;
|
||||
CenterLon=9.0;
|
||||
Planes={};
|
||||
|
||||
function getIconForPlane(plane) {
|
||||
return {
|
||||
strokeWeight: 2,
|
||||
path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
|
||||
scale: 5,
|
||||
fillColor: 'yellow',
|
||||
fillOpacity: 0.8,
|
||||
rotation: plane.track
|
||||
};
|
||||
}
|
||||
|
||||
function fetchData() {
|
||||
$.getJSON('/data.json', function(data) {
|
||||
var stillhere = {}
|
||||
for (var j=0; j < data.length; j++) {
|
||||
var plane = data[j];
|
||||
stillhere[plane.hex] = true;
|
||||
|
||||
if (Planes[plane.hex]) {
|
||||
var myplane = Planes[plane.hex];
|
||||
var marker = myplane.marker;
|
||||
var icon = marker.getIcon();
|
||||
var newpos = new google.maps.LatLng(plane.lat, plane.lon);
|
||||
marker.setPosition(newpos);
|
||||
marker.setIcon(getIconForPlane(plane));
|
||||
} else {
|
||||
var marker = new google.maps.Marker({
|
||||
position: new google.maps.LatLng(plane.lat, plane.lon),
|
||||
map: Map,
|
||||
title: plane.hex,
|
||||
icon: getIconForPlane(plane)
|
||||
});
|
||||
plane.marker = marker;
|
||||
Planes[plane.hex] = plane;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove idle planes. */
|
||||
for (var p in Planes) {
|
||||
if (!stillhere[p]) {
|
||||
Planes[p].marker.setMap(null);
|
||||
delete Planes[p];
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
var mapOptions = {
|
||||
center: new google.maps.LatLng(CenterLat, CenterLon),
|
||||
zoom: 8,
|
||||
mapTypeId: google.maps.MapTypeId.ROADMAP
|
||||
};
|
||||
Map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions);
|
||||
|
||||
/* Setup our timer to poll from the server. */
|
||||
window.setInterval(function() {
|
||||
fetchData();
|
||||
}, 1000);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="initialize()">
|
||||
<div id="map_canvas" style="width:100%; height:100%"></div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue