Skip to content

Instantly share code, notes, and snippets.

@usr-ein
Created March 23, 2021 00:33
Show Gist options
  • Save usr-ein/575f89260a94d89700db019376bccbac to your computer and use it in GitHub Desktop.
Save usr-ein/575f89260a94d89700db019376bccbac to your computer and use it in GitHub Desktop.
Twitter live map of geolocated tweets in an area, streamed to .kml file, which can be viewed in Google Earth
import re
from tempfile import mkstemp
from shutil import move
from os import remove, close
class LiveMap(object):
def __init__(self, livePoints, pathToLive = "live.kml"):
super(LiveMap, self).__init__()
self.livePoints = livePoints
self.pathToLive = pathToLive
def initKMLFile(self):
kmlFile = open(self.pathToLive, "w")
kml = '<?xml version="1.0" encoding="UTF-8"?>\n<kml xmlns="http://www.opengis.net/kml/2.2">\n<Document>'
for livePoint in self.livePoints:
kml += livePoint.kml
kml += '</Document>\n</kml>'
kmlFile.seek(0)
kmlFile.truncate()
kmlFile.write(kml)
kmlFile.close()
def updateMap(self):
print("Point count in map: {}".format(len(self.livePoints)))
#Create temp file
fh, abs_path = mkstemp()
kml = '<?xml version="1.0" encoding="UTF-8"?>\n<kml xmlns="http://www.opengis.net/kml/2.2">\n<Document>'
for livePoint in self.livePoints:
kml += livePoint.kml
kml += '</Document>\n</kml>'
new_file = open(abs_path,'w')
new_file.write(kml)
close(fh)
#Remove original file
try:
remove(self.pathToLive)
except FileNotFoundError:
pass # Happens when pathToLive file doesn't exist, no big deal
#Move new file
move(abs_path, self.pathToLive)
class LivePoint(object):
"""Create live pinpoint on Google Earth from lat/lon
Use: lp = LivePoint(lat, lon, path_to_generate_live_kml, point_s_name) """
def __init__(self, lat, lon, pointName = "LivePoint", pointDesc = "A dynamic point", pointIconURI = ""):
super(LivePoint, self).__init__()
self.kml = ""
self.lat = lat
self.lon = lon
self.pointName = pointName
self.pointDesc = pointDesc
self.pointIconURI = pointIconURI
self.reg = re.compile(r"^.*<coordinates>[0-9\.\,]+<\/coordinates>.*$")
self.initKML()
def initKML(self):
self.kml = (
'<Style id="icon"><IconStyle><Icon><href>{}</href></Icon></IconStyle></Style>\n'
'<Placemark>\n'
'<name>{}</name>\n'
'<description>{}</description>\n'
'<styleUrl>#icon</styleUrl>\n'
'<Point>\n'
'<coordinates>{},{}</coordinates>\n'
'</Point>\n'
'</Placemark>\n'
).format(self.pointIconURI, self.pointName, self.pointDesc, self.lon, self.lat)
def updatePoint(self, nLat, nLon):
self.kml = re.sub(self.reg, "<coordinates>{},{}</coordinates>".format(nLon, nLat), self.kml)
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document><Style id="icon"><IconStyle><Icon><href>http://pbs.twimg.com/profile_images/930226803008069632/2V5iRPf5_normal.jpg</href></Icon></IconStyle></Style>
<Placemark>
<name>geometre18</name>
<description>D E S G E N S . . . et la notion d'oisiveté.
. à Paris, France
Post ==> https://www.instagram.com/p/BhZRq7xloOu/</description>
<styleUrl>#icon</styleUrl>
<Point>
<coordinates>2.3508,48.8567</coordinates>
</Point>
</Placemark>
<Style id="icon"><IconStyle><Icon><href>http://pbs.twimg.com/profile_images/693101853987475457/02UJ5moF_normal.png</href></Icon></IconStyle></Style>
<Placemark>
<name>JulienBinet</name>
<description>#NouvellePP ? à Paris, France
Post ==> https://www.instagram.com/p/BhZR256A62z/</description>
<styleUrl>#icon</styleUrl>
<Point>
<coordinates>2.3508,48.8567</coordinates>
</Point>
</Placemark>
<Style id="icon"><IconStyle><Icon><href>http://pbs.twimg.com/profile_images/785211727088590849/LGGL32XB_normal.jpg</href></Icon></IconStyle></Style>
<Placemark>
<name>Michele_SABBAN</name>
<description>Conférence de presse ce matin pour le lancement de Transitions les 26 et 27 juin à Monaco 🇲🇨 à…
Post ==> https://www.instagram.com/p/BhZR3b4jE0O/</description>
<styleUrl>#icon</styleUrl>
<Point>
<coordinates>2.3508,48.8567</coordinates>
</Point>
</Placemark>
<Style id="icon"><IconStyle><Icon><href>http://pbs.twimg.com/profile_images/969889088395530241/s0HdAeer_normal.jpg</href></Icon></IconStyle></Style>
<Placemark>
<name>eyepreferparis</name>
<description>Conciergerie Please click on my profile link to watch my new video series A Bite of Paris on my…
Post ==> https://www.instagram.com/p/BhZR4rygQN4/</description>
<styleUrl>#icon</styleUrl>
<Point>
<coordinates>2.3461499,48.8560791</coordinates>
</Point>
</Placemark>
<Style id="icon"><IconStyle><Icon><href>http://pbs.twimg.com/profile_images/571097657143377921/W02IGFBl_normal.jpeg</href></Icon></IconStyle></Style>
<Placemark>
<name>delicesdevaness</name>
<description>Elles sont arrivées !! 😍
Dégustées au restaurant_virtus
Sublimées par 2 virtuoses ✨
Je vous…
Post ==> https://www.instagram.com/p/BhZR9-oF2oG/</description>
<styleUrl>#icon</styleUrl>
<Point>
<coordinates>2.37814,48.85023</coordinates>
</Point>
</Placemark>
<Style id="icon"><IconStyle><Icon><href>http://pbs.twimg.com/profile_images/378800000608005637/6bfde6eb41a7edaba00900ac97120824_normal.jpeg</href></Icon></IconStyle></Style>
<Placemark>
<name>Frainke</name>
<description>#OnceUponATime
La Dame de Fer
#Paris, #France (03/2018)
#LaViedArtiste #LatinosByDay #WeekEnd…
Post ==> https://www.instagram.com/p/BhZR8y0nOKR/</description>
<styleUrl>#icon</styleUrl>
<Point>
<coordinates>2.29455382,48.85838631</coordinates>
</Point>
</Placemark>
<Style id="icon"><IconStyle><Icon><href>http://pbs.twimg.com/profile_images/969889088395530241/s0HdAeer_normal.jpg</href></Icon></IconStyle></Style>
<Placemark>
<name>eyepreferparis</name>
<description>Waterfall at La Concergerie Please click on my profile link to watch my new video series A Bite…
Post ==> https://www.instagram.com/p/BhZSDb8gDwl/</description>
<styleUrl>#icon</styleUrl>
<Point>
<coordinates>2.3461499,48.8560791</coordinates>
</Point>
</Placemark>
</Document>
</kml>
import tweepy
import requests
from LiveMap import LivePoint, LiveMap
# Paris: 2.246862,48.812219,2.422987,48.905958
## Auth to Tweeter API ##
consumerKey = "put it here"
consumerSecret = "put it there"
accessToken = "pop it in here"
accessSecret = "pop it in there"
auth = tweepy.OAuthHandler(consumerKey, consumerSecret)
auth.set_access_token(accessToken, accessSecret)
api = tweepy.API(auth)
INSTAGRAM_ONLY = True
livePoints = []
liveMap = LiveMap(livePoints, pathToLive="liveTweet.kml")
## Collect Football related tweets
class CustomStreamListener(tweepy.StreamListener):
def on_status(self, status):
text = status.text
if(status.geo):
print(text)
print("Geo: ", status.geo)
name = status._json['user']['screen_name']
profilePict = status._json['user']['profile_image_url']
link = status.text[-23:]
# Test if the twitter post is an instagram post
if "https" in link:
finalURL = requests.get(link).url
if INSTAGRAM_ONLY and not "instagram" in finalURL:
return
else:
link = finalURL
desc = status.text[:-23] + "\nPost ==> " + link
name = name.replace("&", "")
desc = desc.replace("&", "")
livePoint = LivePoint(status.geo['coordinates'][0], status.geo['coordinates'][1], pointName=name, pointDesc=desc, pointIconURI=profilePict)
livePoints.append(livePoint)
liveMap.updateMap()
print("-"*10)
streamListener = CustomStreamListener()
streamTweet = tweepy.Stream(auth = api.auth, listener=streamListener)
streamTweet.filter(locations=[2.246862,48.812219,2.422987,48.905958]) # Square around Paris
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment