Facebook Places Graph Query based on Bounding Box

The FB Places Graph is open for non-personal data (e.g. place locations, number of checkins, likes, place categories). However, it is limited to radial queries for lat/lng coordinates up to 5000m. If we want to query a bounding box, multiple points need to be queried in a grid and duplicate responses need to be merged. This Notebook will also include preview of data on a Geoviews map and output to HTML/CSV for further analysis.

In [1]:
import geoviews as gv
import geoviews.feature as gf
from pathlib import Path
from shapely.geometry import shape
from shapely.geometry import Point
import geopandas as gp
import pandas as pd
import holoviews as hv
hv.notebook_extension('bokeh')

Create Query Grid

First, create a grid for sequential loading of FB places per centerpoint:

In [2]:
from fiona.crs import from_epsg
from shapely.geometry import Polygon
import numpy as np

# load kreis shape for defining the boundary of grid
kreisgrenze = Path.cwd() / "Shapes" / "westsachsen_wgs1984.shp"
polys = gp.read_file(kreisgrenze)

# Reproject the geometries by replacing the values with projected ones
# We want to use distance in meters to define the grid-size
# use WGS 84 / UTM zone 33N (EPSG Code 32633)
polys['geometry'] = polys['geometry'].to_crs(epsg=32633)
xmin,ymin,xmax,ymax = polys.total_bounds

# define grid size
lenght = 2000
width = 2000

cols = list(range(int(np.floor(xmin)), int(np.ceil(xmax)), width))
rows = list(range(int(np.floor(ymin)), int(np.ceil(ymax)), lenght))
rows.reverse()

polygons = []
for x in cols:
    for y in rows:
        polygons.append( Polygon([(x,y), (x+width, y), (x+width, y-lenght), (x, y-lenght)]) )
# convert polygons to geodataframe
grid = gp.GeoDataFrame({'geometry':polygons})
# set coordinate system of geodataframe
grid.crs = from_epsg(32633)

# project back to WGS1984 for display in geoviews
# and coordinates in decimal degrees (lat, lng)
grid['geometry'] = grid['geometry'].to_crs(epsg=4236)
grid.crs = from_epsg(4236)

Optional save grid to file

grid.to_file(Path.cwd() / "Shapes" / "grid.shp")
In [4]:
print(grid.crs)
print(grid.head())
{'init': 'epsg:4236', 'no_defs': True}
                                            geometry
0  POLYGON ((12.13158479709352 51.66568685650127,...
1  POLYGON ((12.13271926888507 51.64772831908428,...
2  POLYGON ((12.13385256342631 51.62976970875307,...
3  POLYGON ((12.13498468233109 51.61181102552437,...
4  POLYGON ((12.1361156272102 51.59385226941488, ...

Create Geoviews layer from Geodataframe

In [3]:
gridlayer = gv.Polygons(grid)
In [4]:
hv.Overlay(
    gv.tile_sources.Wikipedia * \
    #gv.tile_sources.EsriImagery * \
    gridlayer.opts(alpha=0.5)
    ).opts(
        width=800,
        height=480
    )
Out[4]:

Get centers of polygons with:

In [21]:
print(len(grid))
for index, poly in grid['geometry'].iteritems():
    print(type(poly))
    print(poly.centroid)
    if index > 5:
        break
1520
<class 'shapely.geometry.polygon.Polygon'>
POINT (12.14658872274058 51.65705921984495)
<class 'shapely.geometry.polygon.Polygon'>
POINT (12.14771691679207 51.63910042097886)
<class 'shapely.geometry.polygon.Polygon'>
POINT (12.14884394028053 51.62114154938364)
<class 'shapely.geometry.polygon.Polygon'>
POINT (12.14996979481029 51.60318260507577)
<class 'shapely.geometry.polygon.Polygon'>
POINT (12.15109448198264 51.58522358807165)
<class 'shapely.geometry.polygon.Polygon'>
POINT (12.1522180033959 51.56726449838764)
<class 'shapely.geometry.polygon.Polygon'>
POINT (12.15334036064535 51.54930533604019)

Old approach: use query grid from lat/lng list (from ArcGIS fishnet)

grid_coordinates_path = Path.cwd() / "latlng_fishnet.csv" grid_coordinates = pd.read_csv(grid_coordinates_path)grid_coordinates.head()

Load Facebook Data

Before querying FB places, we need to authenticate with a valid Facebook login. The Token will be used to sign queries.

Get/Renew Access token: https://developers.facebook.com/tools/explorer/145634995501895/

Select App Token

In [8]:
fb_access_token = 'EAACeMtszp0EBABykbhKaZA6QAFdZAMToxWvS5kxfKWE17xqyhOv20dUU0wd0j7he7e8D14j8Jdm6485WYxu8bQI59DILPTnQda9VGfmyTE9NmEHgZAwuyy3ys1X7DpS8iBt0itLtPXXzJ1WjO5BitBrIz5ZAmTGt3nXN2NKy74UgGUPsm0STjAyZCLhCxB1eSLENjr8JwAMK9ZAbEAcfIy'
  • Make sure that query_url is still up to date with current version of FB Place Graph API (e.g. check version number)
  • the value of the circle: The radius of the circle is half the length of the diagonal of the square link
In [6]:
import math
circle_radius = 2000*math.sqrt(2)/2
print(circle_radius)
1414.213562373095

We use 1500, to provide some safety measure and remove any duplicates received. See this link for Place Endpoint Fields. Chekc out this link for current rate limits. There's a limit of 200 calls per hour per app (total). This means we need to limit api calls to 18 requests per Minute.

In [9]:
import requests
import json
query_url = 'https://graph.facebook.com/v3.2/search'
params = dict(
    type='place',
    center='0,0',
    distance=1500,
    fields='name,checkins,picture,about,context,link,location,engagement,is_verified'
           'overall_star_rating,rating_count,hours,description,category_list,single_line_address,cover',
    limit=500,
    access_token=fb_access_token
)

# load for intersection
kreisgrenze = Path.cwd() / "Shapes" / "westsachsen_wgs1984.shp"
kreis_polys = gp.read_file(kreisgrenze)

Retrieve Data

In [8]:
import time
# enter here start and end index
# from_index is the first index that will be parsed
# to_index is the last index that will be parsed
from_index = 1265
to_index = 1600

for index, poly in grid['geometry'].iteritems():
    if index < from_index:
        # skip already existing
        continue
    if not any(kreis_polys.intersects(poly)):
        # skip grid cells that are not in Westsachsen
        continue
    grid_center = poly.centroid
    params['center'] = f'{grid_center.y},{grid_center.x}'
    successful = False
    while not successful:
        response = requests.get(url=query_url, params=params)
        json_data = json.loads(response.text)
        if json_data.get("error") and (
            json_data.get("error").get("code") == 4 or json_data.get("error").get("code") == 613):
            # rate limit notice
            print("Waiting 2 Minutes for Rate Limit")
            time.sleep(120)
        else:
            successful = True
    # process results
    with open(Path.cwd() / 'JSON_Data' / f'{index:03d}_json.json', 'w') as outfile:  
        json.dump(json_data, outfile)
    # output reporting    
    places_json = json.loads(response.text)["data"]
    print(f'Retrieved {len(places_json)} places for grid {index:03d} ({grid_center.y}, {grid_center.x})')
    time.sleep(25)
    if index > to_index:
        break  
Retrieved 57 places for grid 1265 (51.22613038640947, 13.061299420773533)
Retrieved 10 places for grid 1266 (51.2081582525925, 13.06205440389858)
Retrieved 0 places for grid 1282 (51.639949737838464, 13.072599359924622)
Retrieved 0 places for grid 1283 (51.62197875750678, 13.07336112767197)
Retrieved 0 places for grid 1284 (51.6040077139302, 13.074122104762415)
Retrieved 26 places for grid 1285 (51.58603660711152, 13.074882292280222)
Retrieved 58 places for grid 1286 (51.56806543705359, 13.07564169130762)
Retrieved 7 places for grid 1287 (51.55009420375924, 13.0764003029248)
Retrieved 5 places for grid 1288 (51.53212290723132, 13.077158128209918)
Retrieved 8 places for grid 1289 (51.51415154747264, 13.077915168239121)
Retrieved 13 places for grid 1290 (51.49618012448605, 13.078671424086528)
Retrieved 5 places for grid 1291 (51.47820863827439, 13.079426896824254)
Retrieved 0 places for grid 1292 (51.46023708884049, 13.080181587522395)
Retrieved 2 places for grid 1293 (51.442265476187195, 13.080935497249056)
Retrieved 7 places for grid 1294 (51.42429380031733, 13.08168862707031)
Retrieved 14 places for grid 1295 (51.406322061233766, 13.082440978050286)
Retrieved 12 places for grid 1296 (51.388350258939305, 13.083192551251077)
Retrieved 20 places for grid 1297 (51.37037839343683, 13.083943347732816)
Retrieved 17 places for grid 1298 (51.35240646472917, 13.084693368553648)
Retrieved 10 places for grid 1299 (51.33443447281916, 13.085442614769741)
Retrieved 23 places for grid 1300 (51.31646241770963, 13.086191087435278)
Retrieved 81 places for grid 1301 (51.29849029940347, 13.086938787602511)
Retrieved 13 places for grid 1302 (51.280518117903505, 13.087685716321689)
Retrieved 20 places for grid 1303 (51.26254587321261, 13.088431874641119)
Retrieved 21 places for grid 1304 (51.2445735653336, 13.089177263607166)
Retrieved 7 places for grid 1305 (51.22660119426934, 13.089921884264214)
Retrieved 5 places for grid 1306 (51.20862876002269, 13.090665737654737)
Retrieved 0 places for grid 1323 (51.62244913827669, 13.102232163191498)
Retrieved 2 places for grid 1324 (51.60447779360332, 13.102981753704837)
Retrieved 0 places for grid 1325 (51.58650638592437, 13.103730566440104)
Retrieved 1 places for grid 1326 (51.56853491524238, 13.104478602463406)
Retrieved 3 places for grid 1327 (51.55056338155982, 13.10522586283886)
Retrieved 23 places for grid 1328 (51.53259178487919, 13.105972348628546)
Retrieved 6 places for grid 1329 (51.514620125202974, 13.106718060892577)
Retrieved 13 places for grid 1330 (51.49664840253369, 13.107463000689084)
Retrieved 73 places for grid 1331 (51.47867661687385, 13.108207169074177)
Retrieved 12 places for grid 1332 (51.460704768225916, 13.10895056710204)
Retrieved 9 places for grid 1333 (51.442732856592414, 13.10969319582483)
Retrieved 3 places for grid 1334 (51.42476088197584, 13.11043505629279)
Retrieved 9 places for grid 1335 (51.40678884437871, 13.111176149554131)
Retrieved 30 places for grid 1336 (51.388816743803545, 13.111916476655171)
Retrieved 12 places for grid 1337 (51.37084458025283, 13.112656038640225)
Retrieved 6 places for grid 1338 (51.35287235372908, 13.113394836551656)
Retrieved 18 places for grid 1339 (51.33490006423484, 13.11413287142993)
Retrieved 43 places for grid 1340 (51.31692771177256, 13.11487014431351)
Retrieved 93 places for grid 1341 (51.29895529634482, 13.115606656238954)
Retrieved 25 places for grid 1342 (51.280982817954104, 13.11634240824087)
Retrieved 22 places for grid 1343 (51.26301027660298, 13.117077401351967)
Retrieved 13 places for grid 1344 (51.24503767229391, 13.117811636602976)
Retrieved 9 places for grid 1345 (51.22706500502946, 13.11854511502277)
Retrieved 4 places for grid 1346 (51.209092274812136, 13.119277837638258)
Retrieved 5 places for grid 1363 (51.62291242252008, 13.131103977558539)
Retrieved 14 places for grid 1364 (51.60494078128641, 13.13184218042664)
Retrieved 8 places for grid 1365 (51.586969077280166, 13.13257961731327)
Retrieved 0 places for grid 1366 (51.568997310503505, 13.133316289268397)
Retrieved 2 places for grid 1367 (51.55102548095859, 13.134052197340017)
Retrieved 25 places for grid 1368 (51.53305358864756, 13.134787342574164)
Retrieved 12 places for grid 1369 (51.51508163357262, 13.13552172601491)
Retrieved 16 places for grid 1370 (51.49710961573591, 13.136255348704365)
Retrieved 90 places for grid 1371 (51.4791375351396, 13.136988211682686)
Retrieved 10 places for grid 1372 (51.46116539178584, 13.137720315988068)
Retrieved 2 places for grid 1373 (51.443193185676876, 13.13845166265681)
Retrieved 4 places for grid 1374 (51.425220916814794, 13.139182252723197)
Retrieved 4 places for grid 1375 (51.40724858520183, 13.13991208721964)
Retrieved 29 places for grid 1376 (51.38927619084013, 13.140641167176582)
Retrieved 4 places for grid 1377 (51.371303733731914, 13.14136949362257)
Retrieved 5 places for grid 1378 (51.35333121387932, 13.1420970675842)
Retrieved 6 places for grid 1379 (51.335358631284564, 13.142823890086147)
Retrieved 7 places for grid 1380 (51.31738598594982, 13.143549962151221)
Retrieved 19 places for grid 1381 (51.299413277877306, 13.144275284800257)
Retrieved 22 places for grid 1382 (51.2814405070692, 13.144999859052248)
Retrieved 11 places for grid 1383 (51.26346767352768, 13.14572368592423)
Retrieved 7 places for grid 1384 (51.24549477725498, 13.146446766431385)
Retrieved 6 places for grid 1385 (51.227521818253294, 13.147169101586977)
Retrieved 2 places for grid 1404 (51.605396676538, 13.160703373131257)
Retrieved 5 places for grid 1405 (51.58742468073794, 13.161429433119281)
Retrieved 2 places for grid 1406 (51.56945262239657, 13.16215473995822)
Retrieved 2 places for grid 1407 (51.551480501515705, 13.162879294679982)
Retrieved 1 places for grid 1408 (51.53350831809718, 13.163603098314521)
Retrieved 6 places for grid 1409 (51.51553607214283, 13.164326151889867)
Retrieved 3 places for grid 1410 (51.4975637636545, 13.16504845643212)
Retrieved 2 places for grid 1411 (51.47959139263404, 13.165770012965451)
Retrieved 12 places for grid 1412 (51.461618959083275, 13.16649082251212)
Retrieved 11 places for grid 1413 (51.443646463004065, 13.167210886092457)
Retrieved 8 places for grid 1414 (51.425673904398245, 13.167930204724913)
Retrieved 1 places for grid 1415 (51.40770128326768, 13.168648779425984)
Retrieved 17 places for grid 1416 (51.389728599614244, 13.169366611210314)
Retrieved 2 places for grid 1417 (51.37175585343976, 13.170083701090624)
Retrieved 3 places for grid 1418 (51.3537830447461, 13.17080005007774)
Retrieved 2 places for grid 1419 (51.33581017353516, 13.171515659180617)
Retrieved 22 places for grid 1420 (51.31783723980877, 13.172230529406297)
Retrieved 15 places for grid 1421 (51.29986424356881, 13.172944661759985)
Retrieved 8 places for grid 1422 (51.28189118481716, 13.173658057244966)
Retrieved 7 places for grid 1423 (51.26391806355571, 13.174370716862676)
Retrieved 12 places for grid 1424 (51.24594487978632, 13.175082641612692)
Retrieved 17 places for grid 1425 (51.227971633510876, 13.175793832492714)
Retrieved 1 places for grid 1447 (51.551928442798065, 13.191707143108502)
Retrieved 0 places for grid 1448 (51.53395597279547, 13.192419604115415)
Retrieved 8 places for grid 1449 (51.51598344048162, 13.193131326799264)
Retrieved 2 places for grid 1450 (51.498010845858026, 13.19384231217014)
Retrieved 8 places for grid 1451 (51.48003818892626, 13.19455256123623)
Retrieved 8 places for grid 1452 (51.46206546968779, 13.19526207500383)
Retrieved 17 places for grid 1453 (51.44409268814416, 13.195970854477356)
Retrieved 20 places for grid 1454 (51.426119844296885, 13.196678900659332)
Retrieved 5 places for grid 1455 (51.40814693814753, 13.197386214550425)
Retrieved 16 places for grid 1456 (51.39017396969763, 13.198092797149412)
Retrieved 3 places for grid 1457 (51.37220093894871, 13.198798649453217)
Retrieved 14 places for grid 1458 (51.35422784590231, 13.19950377245688)
Retrieved 2 places for grid 1459 (51.33625469055998, 13.200208167153612)
Retrieved 26 places for grid 1460 (51.31828147292329, 13.200911834534741)
Retrieved 8 places for grid 1461 (51.30030819299378, 13.201614775589755)
Retrieved 8 places for grid 1462 (51.282334850773005, 13.2023169913063)
Retrieved 12 places for grid 1463 (51.26436144626256, 13.20301848267018)
Retrieved 46 places for grid 1464 (51.246387979463975, 13.203719250665346)
Retrieved 17 places for grid 1465 (51.22841445037882, 13.204419296273942)
Retrieved 0 places for grid 1489 (51.516423738163645, 13.22193723902298)

Retrieve Category Data

This query will list all FB page categories and respective hierarchy.

In [10]:
query_url = 'https://graph.facebook.com/v3.2/fb_page_categories'
params = dict(
    access_token=fb_access_token
)
response = requests.get(url=query_url, params=params)
json_data = json.loads(response.text)
In [12]:
with open(Path.cwd() / f'FB_categories_root_json.json', 'w') as outfile:  
        json.dump(json_data, outfile)

List all categories

In [20]:
for main_cat in json_data["data"]:
    print(f'ID {main_cat.get("id")} - {main_cat.get("name")}')
    print('      Subcats:')
    subcats = main_cat.get('fb_page_categories')
    if subcats:
        for sub_cat in subcats: 
              print(f'      ID {sub_cat.get("id")} - {sub_cat.get("name")}')
ID 1500 - Interest
      Subcats:
      ID 856055631167537 - Literary Arts
      ID 756092301147942 - Performance Art
      ID 1758092431143387 - Performing Arts
      ID 2900 - Science
      ID 964585346994407 - Sports
      ID 1080612391976317 - Visual Arts
ID 152880021441864 - Community Organization
      Subcats:
      ID 188166377871384 - Armed Forces
      ID 226326230802065 - Charity Organization
      ID 186004504854452 - Country Club / Clubhouse
      ID 170968163319233 - Community Service
      ID 191523214199822 - Environmental Conservation Organization
      ID 161422927240513 - Government Organization
      ID 192775991124365 - Labor Union
      ID 373543049350668 - Political Organization
      ID 2618 - Political Party
      ID 314375185582911 - Private Members Club
      ID 187714557925874 - Religious Organization
      ID 215343825145859 - Social Club
      ID 165264720195968 - Sorority & Fraternity
      ID 189018581118681 - Sports Club
      ID 181053558607965 - Youth Organization
ID 1314020451960517 - Media
      Subcats:
      ID 2903 - Art
      ID 979978068761972 - Books & Magazines
      ID 1208 - Concert Tour
      ID 1724531564506407 - Media Restoration Service
      ID 1290986887644410 - Show
      ID 1205 - Music
      ID 943469559038367 - Theatrical Play
      ID 1784293938474260 - Theatrical Productions
      ID 1728442777392577 - TV & Movies
ID 1602 - Public Figure
      Subcats:
      ID 1602034176774683 - Actor
      ID 1601 - Artist
      ID 1600 - Athlete
      ID 1301 - Author
      ID 792007097567368 - Band
      ID 180164648685982 - Musician/Band
      ID 361282040719868 - Blogger
      ID 1606 - Chef
      ID 1610 - Comedian
      ID 1614 - Dancer
      ID 1615 - Designer
      ID 2347428775505624 - Digital Creator
      ID 1617 - Entrepreneur
      ID 1784467105117322 - Fashion Model
      ID 502966423232592 - Film Director
      ID 301500100194179 - Fitness Model
      ID 1701 - Government Official
      ID 622132518208039 - Editor
      ID 1604 - Journalist
      ID 1720316034885300 - Motivational Speaker
      ID 1109 - Writer
      ID 1335670856447673 - Musician
      ID 1605 - News Personality
      ID 103436486409265 - Orchestra
      ID 1108 - Producer
      ID 471120789926333 - Gamer
      ID 494338820719492 - Scientist
      ID 1888910144659710 - Spiritual Leader
      ID 124584130946507 - Sports Promoter
      ID 1030515153650195 - Talent Agent
      ID 1817904601806120 - Video Creator
ID 1896171907331974 - Businesses
      Subcats:
      ID 1757592557789532 - Advertising/Marketing
      ID 1574325646194878 - Agriculture
      ID 243290832429433 - Commercial & Industrial
      ID 2250 - Education
      ID 1022050661163852 - Finance
      ID 1562965077339698 - Food & Beverage
      ID 505091123022329 - Hotel & Lodging
      ID 241113486274430 - Legal
      ID 1758418281071392 - Local Service
      ID 2233 - Media/News Company
      ID 145118935550090 - Medical & Health
      ID 2235 - Non-Governmental Organization (NGO)
      ID 2603 - Nonprofit Organization
      ID 147714868971098 - Public & Government Service
      ID 297544187300691 - Science, Technology & Engineering
      ID 139225689474222 - Beauty, Cosmetic & Personal Care
      ID 133436743388217 - Arts & Entertainment
      ID 180410821995109 - Automotive, Aircraft & Boat
      ID 198327773511962 - Real Estate
      ID 200600219953504 - Shopping & Retail
      ID 186982054657561 - Sports & Recreation
      ID 128232937246338 - Travel & Transportation
ID 683513901834713 - Non-Business Places
      Subcats:
      ID 1057744077629687 - Automated Teller Machine (ATM)
      ID 216161991738736 - Campus Building
      ID 1713595685546855 - City Infrastructure
      ID 1032965636792826 - Government Building
      ID 1874409019452971 - Locality
      ID 210261102322291 - Meeting Room
      ID 635235176664335 - Outdoor Recreation
      ID 1384896128224370 - Public Toilet
      ID 2607 - Religious Place of Worship
      ID 192049437499122 - Residence
      ID 209889829023118 - Landmark & Historical Place
ID 369730359717478 - Other
      Subcats:
      ID 1605186416478696 - Brand
      ID 2606 - Cause
      ID 3001 - Color
      ID 2612 - Community
      ID 1901 - Cuisine
      ID 3003 - Diseases
      ID 1702 - Election
      ID 188159097871782 - Exchange Program
      ID 3010 - Harmonized Page
      ID 2403 - Language
      ID 2904 - Mood
      ID 3009 - Nationality
      ID 134694380732246 - University (NCES)
      ID 2620 - Political Ideology
      ID 2623 - Profile
      ID 2619 - Course
      ID 1806 - Sports Season
      ID 3004 - Surgeries
      ID 166975666684060 - Ticket Sales
      ID 2614 - Topic
      ID 192119584190796 - Event
      ID 129417183848258 - Just For Fun
      ID 1059544340792177 - TypeAhead
      ID 2635 - University Status
      ID 2611 - Work Position
      ID 2617 - Work Project
      ID 2636 - Work Status
ID 160648811266809 - Feed for Workplace
      Subcats:

These categories appear to be of some interest to planning:

ID 964585346994407 - Sports
ID 226326230802065 - Charity Organization
ID 186004504854452 - Country Club / Clubhouse
ID 170968163319233 - Community Service
ID 191523214199822 - Environmental Conservation Organization
ID 189018581118681 - Sports Club
ID 181053558607965 - Youth Organization
ID 2235 - Non-Governmental Organization (NGO)
ID 2603 - Nonprofit Organization
ID 147714868971098 - Public & Government Service
ID 133436743388217 - Arts & Entertainment
ID 186982054657561 - Sports & Recreation
Main: ID 683513901834713 - Non-Business Places
ID 1713595685546855 - City Infrastructure
ID 1874409019452971 - Locality
ID 635235176664335 - Outdoor Recreation
ID 2607 - Religious Place of Worship
ID 209889829023118 - Landmark & Historical Place
In [ ]:
planning_cats = ['964585346994407', '226326230802065','186004504854452','170968163319233','191523214199822','189018581118681','181053558607965','2235',
                '2603','147714868971098','133436743388217','186982054657561','683513901834713','1713595685546855','1874409019452971','635235176664335',
                '2607','209889829023118']

Load all places from all retrieved json files

In [3]:
import os
import json
placelist = []
for file in os.listdir('JSON_Data'):
    if file.endswith(".json"):
        with open(Path.cwd() / 'JSON_Data' / file, 'r') as infile: 
            json_data = json.load(infile)
            placelist += json_data["data"]
In [4]:
len(placelist)
Out[4]:
31585
In [5]:
from collections import namedtuple
PlaceTuple = namedtuple('PlaceTuple', 'Latitude Longitude PlaceGuid PlaceName Checkins Likes URL PlaceCats Caption City Country Street Zip')
place_tuples = []
skippedCount = 0
place_already_inserted = set()
In [10]:
for place in placelist:
    #print(place)
    if place.get('name'): 
        iLocName = place["name"]
    else:
        # skip problematic entries
        skippedCount += 1
        continue
    if place.get('checkins'): 
        iLocCheckins =  place["checkins"] if isinstance(
            place["checkins"], int) else None
    else:
        iLocCheckins = None
    if place.get('engagement'): 
        iLocLikes =  place["engagement"]["count"] if isinstance(
            place["engagement"]["count"], int) else None
    else:
        iLocLikes = 0                      
    if place.get('description'):
        iLocDescription = place["description"]#.replace(
            #'\n', ' ').replace('\r', ' ')#[:50]
    elif place.get('about'):
        iLocDescription = place["about"]
    else:
        iLocDescription = None
    if place.get('id'):
        place_guid = place["id"]
        if place_guid in place_already_inserted:
            # skip duplicates
            skippedCount += 1
            continue
        else:
            place_already_inserted.add(place_guid)
    else:
        # skip problematic entries
        skippedCount += 1
        continue
    if place.get('category_list'):
        place_types = []
        for cat in place["category_list"]:
            #print(cat.get('id'))
            #print(cat.get('name'))
            #print(type(cat))
            place_types.append(tuple((cat.get('id'), cat.get('name'))))
    else:
        place_types = None
    #print(type(place_type))
    if place.get('location'):
        if place.get('location', {}).get('city'):
            iLocCityName = place["location"]["city"]
        else:
            iLocCityName = None                        
        iLocCityID = None
        if place.get('location', {}).get('country'):
            iLocCountryName = place["location"]["country"]
        else:
            iLocCountryName = None                          
        iLocCountryID = None
        iLocLat = float(place["location"]["latitude"]) #Decimal(place["location"]["latitude"]) 
        iLocLng = float(place["location"]["longitude"]) #Decimal() 
        
        if not any(kreis_polys.intersects(Point(iLocLng, iLocLat))):
            continue
        if place.get('location', {}).get('street'):
            iLocStreet = place["location"]["street"]
        else:
            iLocStreet = None
        if place.get('location', {}).get('zip'):
            iLocZip = place["location"]["zip"]
        else:
            iLocZip = None                            
    else:
        count_non_geotagged += 1
        photo_locID = None
        photo_locName = None
        photo_geoaccuracy = None
        # skip non geotagged
        if excludeWhereGeoInfo_isMissing:
            continue             
    if place.get('link'):
        #place_shortcode = place["link"].rsplit('/',2)[1] 
        place_url = place["link"]
    else:
        place_url = None

    place_tuples.append(
        PlaceTuple(iLocLat, #Latitude = 1
                   iLocLng, #Longitude = 2
                   place_guid,#PlaceGuid = 3
                   iLocName,#PlaceName = 4
                   iLocCheckins, #Checkins = 5
                   int(iLocLikes),#Likes = 6
                   place_url,#URL = 7
                   "".join(";" + str(x[0]) + ":" + str(
                       x[1]) + ";" for x in place_types if not place_types is None).replace(";;",";"), #PlaceCats = 9
                   iLocDescription,#Caption = 11
                   iLocCityName,#City = 12
                   iLocCountryName,#Country = 13
                   iLocStreet,#Street = 14
                   iLocZip,#Zip = 15
                   ))
print(f'{len(place_tuples)} places extracted. Skipped {skippedCount} (e.g. duplicate entries)')
14464 places extracted. Skipped 11521 (e.g. duplicate entries)
In [11]:
pd_place_tuples = pd.DataFrame(place_tuples)

Preview Density

In [21]:
geometry_point = [Point(xy) for xy in zip(pd_place_tuples.Longitude, pd_place_tuples.Latitude)]
pd_place_tuples = pd_place_tuples.drop(['Longitude', 'Latitude'], axis=1)
crs_1984 = {'init': 'epsg:4326'}
gdf = gp.GeoDataFrame(pd_place_tuples, crs=crs_1984, geometry=geometry_point)

dfsjoin = gp.sjoin(kreis_polys, gdf)
In [23]:
dfsjoin.head()
Out[23]:
Id geometry index_right PlaceGuid PlaceName Checkins Likes URL PlaceCats Caption City Country Street Zip
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 777 167628193985060 Jänichen Eduard NaN 0 https://www.facebook.com/pages/J%C3%A4nichen-E... ;149998721725634:Automotive Repair Shop; None Dommitzsch Germany Leipziger Str. 62 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 790 1259730977453247 PENNY NaN 0 https://www.facebook.com/PENNY-1259730954119916/ ;200600219953504:Shopping & Retail; None Dommitzsch Germany Leipziger Straße 22A 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 767 897775410277616 Mahlitzsch 2.0 7 https://www.facebook.com/pages/Mahlitzsch/6326... ;209889829023118:Landmark & Historical Place;2... Mahlitzsch ist ein Ortsteil der Stadt Dommitzs... None None None None
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 765 629235830770577 Förderverein FreiwilligenFeuerwehr Roitzsch e. V. 1.0 0 https://www.facebook.com/pages/F%C3%B6rdervere... ;152880021441864:Community Organization; None Dommitzsch Germany Torgauer Str. 24 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 799 157507177603402 Restaurant Tascos Dart-Kneipe 23.0 11 https://www.facebook.com/pages/Restaurant-Tasc... ;2500:Local Business; None Dommitzsch Germany Leipziger Str. 70 04880
In [26]:
def bin_the_midpoints(bins, midpoints):
    b = bins.copy()
    m = midpoints.copy()
    reindexed = b.reset_index().rename(columns={'index':'bins_index'})
    joined = gp.tools.sjoin(reindexed, m)
    bin_stats = joined.groupby('bins_index')['PlaceGuid'].agg({'fold': len, 'PlaceGuid': np.min})
    return gp.GeoDataFrame(b.join(bin_stats))

bin_stats = bin_the_midpoints(kreis_polys, gdf)
/home/alex/miniconda3/envs/jupyter_env/lib/python3.6/site-packages/ipykernel_launcher.py:6: FutureWarning: using a dict on a Series for aggregation
is deprecated and will be removed in a future version
  
In [27]:
bin_stats.head()
Out[27]:
Id geometry fold PlaceGuid
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 14464 1000215160100252
In [22]:
from holoviews import dim, opts

def set_active_tool(plot, element):
    # enable wheel_zoom by default
    plot.state.toolbar.active_scroll = plot.state.tools[0]

hv.Overlay(
    gv.tile_sources.EsriImagery.opts(alpha=0.5) * \
    gv.Polygons(dfsjoin).opts(fill_alpha=0, color='SelCat')
    ).opts(
    finalize_hooks=[set_active_tool],
    width=1000,
    height=480,
    )
Out[22]:
Id geometry index_right PlaceGuid PlaceName Checkins Likes URL PlaceCats Caption City Country Street Zip
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 777 167628193985060 Jänichen Eduard NaN 0 https://www.facebook.com/pages/J%C3%A4nichen-E... ;149998721725634:Automotive Repair Shop; None Dommitzsch Germany Leipziger Str. 62 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 790 1259730977453247 PENNY NaN 0 https://www.facebook.com/PENNY-1259730954119916/ ;200600219953504:Shopping & Retail; None Dommitzsch Germany Leipziger Straße 22A 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 767 897775410277616 Mahlitzsch 2.0 7 https://www.facebook.com/pages/Mahlitzsch/6326... ;209889829023118:Landmark & Historical Place;2... Mahlitzsch ist ein Ortsteil der Stadt Dommitzs... None None None None
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 765 629235830770577 Förderverein FreiwilligenFeuerwehr Roitzsch e. V. 1.0 0 https://www.facebook.com/pages/F%C3%B6rdervere... ;152880021441864:Community Organization; None Dommitzsch Germany Torgauer Str. 24 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 799 157507177603402 Restaurant Tascos Dart-Kneipe 23.0 11 https://www.facebook.com/pages/Restaurant-Tasc... ;2500:Local Business; None Dommitzsch Germany Leipziger Str. 70 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 801 582719461785354 Tierarztpraxis Dr. Silke Geßwein 4.0 162 https://www.facebook.com/Tierarztpraxis-Dr-Sil... ;162068413843305:Veterinarian;145118935550090:... Samstag und in Notfällen nach Vereinbarung\n\n... Dommitzsch Germany Straße der Jugend 17 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 741 1744316672282533 Dommitzscher SV Grün Weiß - Volleyball NaN 177 https://www.facebook.com/Dommitzscher-SV-Grün-... ;1500:Interest; Wilkommen bei der 1. Männermannschaft des Domm... Dommitzsch Germany Leipziger Straße None
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 775 137097153590131 FLEISCHEREI Bachmann GmbH NaN 1 https://www.facebook.com/pages/FLEISCHEREI-Bac... ;181564868547915:Butcher Shop; None Dommitzsch Germany Leipziger Str. 18 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 748 291306161416979 Sportfreunde DVS NaN 68 https://www.facebook.com/Sportfreunde-DVS-2913... ;189018581118681:Sports Club; None Dommitzsch Germany None None
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 744 410273539543690 Bürgermeisterkandidat Henrik Bock NaN 0 https://www.facebook.com/pages/B%C3%BCrgermeis... ;110249975723427:Office Supplies; None Dommitzsch Germany Straße der Jugend 24 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 769 1048055011979749 Elblandbahn e.V. NaN 125 https://www.facebook.com/elblandbahn/ ;2603:Nonprofit Organization;2025086974383657:... Reaktivierung der Bahntrasse zwischen Pretzsch... Dommitzsch Germany Leipziger Str. 80 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 798 304944933353008 Postfiliale Lotto- Post- Schreib Spielwaren NaN 0 https://www.facebook.com/pages/Postfiliale-Lot... ;147714868971098:Public & Government Service; None Dommitzsch Germany Leipziger Str. 13 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 751 122734211849968 Pössl Meinhard NaN 1 https://www.facebook.com/pages/P%C3%B6ssl-Mein... ;199182823425834:Electrician; None Dommitzsch Germany Ritterstr. 1 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 807 737687939702036 HAARschneiderei 13.0 130 https://www.facebook.com/DIANA.HAUGK321983/ ;174177802634376:Hair Salon;169758603141095:Ba... "Wohlfühlen & Entspannen"\nEs ist ein originel... Dommitzsch Germany Leipziger Str. 10 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 789 384897321935046 Hartleb Isolde NaN 0 https://www.facebook.com/pages/Hartleb-Isolde/... ;199182823425834:Electrician; None Dommitzsch Germany Leipziger Str. 83 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 759 1286542808080604 Museum der Stadt Dommitzsch NaN 0 https://www.facebook.com/pages/Museum-der-Stad... ;197817313562497:Museum; None Dommitzsch Germany Torgauer Straße 39 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 783 1252135708239422 Deutsche Post NaN 0 https://www.facebook.com/pages/Deutsche-Post/1... ;175838732461622:Post Office; None Dommitzsch Germany Leipziger Straße 6 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 802 735043736673329 Harmonie Kosmetikstudio Kathleen Wagner, 04880... 12.0 41 https://www.facebook.com/pages/Harmonie-Kosmet... ;1644814599176740:Skin Care Service; None Dommitzsch Germany Leipziger STRAßE 6 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 760 1593179320746719 Dienstleistungsunternehmen Weiß GmbH NaN 0 https://www.facebook.com/pages/Dienstleistungs... ;196639807029364:Demolition & Excavation Company; None Dommitzsch Germany Torgauer Str. 41 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 779 179386379468049 Kreissparkasse Torgau Oschatz NaN 0 https://www.facebook.com/pages/Kreissparkasse-... ;133576170041936:Bank; None Dommitzsch Germany Pretzscher Str. 1 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 772 194309364411269 Sparkasse Leipzig 1.0 3 https://www.facebook.com/pages/Sparkasse-Leipz... ;133576170041936:Bank; None Dommitzsch Germany 1 Pretzscher Straße 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 761 1725043844438722 Schlüsseldienst Dommitzsch - 034223 15 97 93 NaN 0 https://www.facebook.com/Schlüsseldienst-Dommi... ;181814521855864:Locksmith; Schlüsseldienst\n Schlüsselnotdienst\n Einbru... Dommitzsch Germany . 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 740 209560855740892 Dommitzsch 1533.0 246 https://www.facebook.com/pages/Dommitzsch/1061... ;2404:City;2401:City; Dommitzsch is a town in the district Nordsachs... Dommitzsch Germany None 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 745 123664851756404 Fischer Olaf NaN 0 https://www.facebook.com/pages/Fischer-Olaf/12... ;161516070564222:Financial Service; None Dommitzsch Germany Ritterstr. 2-1 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 797 261977337666827 Betriebswirtschaftliche Software GmbH (BS) NaN 0 https://www.facebook.com/pages/Betriebswirtsch... ;1065597503495311:Software Company;2256:Intern... None Dommitzsch Germany Grüne Str. 6 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 809 268290333262969 Gaststätte "Lindeneck"Dommitzsch 6.0 111 https://www.facebook.com/GaststatteLindeneckDo... ;164049010316507:Gastropub; None Dommitzsch Germany Torgauer str.1 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 806 496350623802855 Ratskeller Dommitzsch 36.0 69 https://www.facebook.com/Ratskeller-Dommitzsch... ;150217168372274:German Restaurant;19787139022... Gerichte aus Deutschland, Italien und Indien Dommitzsch Germany Markt 1 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 794 1460494544176203 Ratskeller Restaurant NaN 58 https://www.facebook.com/Ratskellerrestaurant/ ;197871390225897:Cafe;150217168372274:German R... The best restaurants, cafes and pubs for casua... Dommitzsch Germany Markt.1, 04880 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 757 232764410533223 Stadtverwaltung NaN 0 https://www.facebook.com/pages/Stadtverwaltung... ;169896676390222:Library; None Dommitzsch Germany Markt 1 04880
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 782 1601334169980029 Stadtkirche St. Marien 1.0 0 https://www.facebook.com/pages/Stadtkirche-St-... ;192134360811676:Church; None Dommitzsch Germany Markt 04880
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10571 654126794775876 Töpferei Gundula Müller 1.0 1 https://www.facebook.com/pages/T%C3%B6pferei-G... ;187133811318958:Business Service; None Kohren-Sahlis Germany Steingasse 120 04655
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10579 222208618494187 Burg Gnandstein 5.0 2 https://www.facebook.com/pages/Burg-Gnandstein... ;273819889375819:Restaurant; None Kohren-Sahlis Germany Gnandsteiner Straße 04654
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10540 208351032532701 Kohren-Sahlis 1953.0 228 https://www.facebook.com/pages/Kohren-Sahlis/1... ;2404:City;2401:City; Kohren-Sahlis is a town in the Leipzig distric... Kohren-Sahlis Germany None 04655
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10580 321774641305726 Jugendfeuerwehr Kohren-Sahlis 11.0 163 https://www.facebook.com/JFKohrenSahlis/ ;2603:Nonprofit Organization; Wir möchten mit Spaß und Teamgeist unseren Nac... Kohren-Sahlis Germany Am Bahnhof 9 04654 Frohburg
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10561 1830131240409670 Städt. Feuerwehr Kohren-Sahlis NaN 1 https://www.facebook.com/pages/St%C3%A4dt-Feue... ;110218445719092:Security Guard Service; None Kohren-Sahlis Germany 9 Am Bahn Hof 04654
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10558 1679412308740128 COSIMO GmbH NaN 0 https://www.facebook.com/pages/COSIMO-GmbH/167... ;187937741228885:Electronics Store; None Kohren-Sahlis Germany Markt 129 04655
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10556 449081172102450 Hoffmann'sche Sammlung NaN 0 https://www.facebook.com/pages/Hoffmannsche-Sa... ;197817313562497:Museum; None Frohburg Germany Markt 71 04655
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10549 282525778842017 Arnold Lichttechnik NaN 0 https://www.facebook.com/pages/Arnold-Lichttec... ;196820813663200:Lighting Store; None Frohburg Germany Karl-Marx-Straße 204 04654
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10563 252308408570467 Töpfermuseum Kohren-Sahlis 5.0 0 https://www.facebook.com/pages/T%C3%B6pfermuse... ;197817313562497:Museum; None Kohren-Sahlis Germany 18 Baumgarten straße 04655
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10545 2087658324603385 Museen Kohren-Sahlis NaN 72 https://www.facebook.com/Museen-Kohren-Sahlis-... ;197817313562497:Museum; Museen Kohren-Sahlis (Stadt Frohburg): Töpferm... Frohburg Germany Baumgartenstr. 3 04654
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10557 1658669997763144 Ratskeller NaN 0 https://www.facebook.com/pages/Ratskeller/1658... ;273819889375819:Restaurant; None Frohburg Germany Markt 69 04655
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10559 1754144298031507 Eiscafe Elisenhof NaN 0 https://www.facebook.com/pages/Eiscafe-Elisenh... ;197871390225897:Cafe; None Kohren-Sahlis Germany None None
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10572 126959648166026 Sparkasse Leipzig 1.0 0 https://www.facebook.com/pages/Sparkasse-Leipz... ;133576170041936:Bank;1057744077629687:Automat... None Frohburg Germany Markt 68 04655
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10574 734328916727794 Ferienwohnung Jagdhütte Kohren NaN 0 https://www.facebook.com/pages/Ferienwohnung-J... ;164243073639257:Hotel; None Frohburg Germany Töpferstraße 1c 04655
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10570 1915173931843726 Zauberer KALU NaN 1 https://www.facebook.com/pages/Zauberer-KALU/1... ;197384240287028:Art Gallery; None Kohren-Sahlis Germany Töpferplatz 04654
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10577 216560281759000 Kohren Sahlis Eiscafé am Töpferbrunnen 31.0 6 https://www.facebook.com/pages/Kohren-Sahlis-E... ;200863816597800:Ice Cream Shop; None Kohren-Sahlis Germany Markt 134 04655
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10567 407931566639777 Burgruine Kohren NaN 0 https://www.facebook.com/pages/Burgruine-Kohre... ;197097220301977:Historical Place; None None None None None
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10575 1895990410677910 Töpferbrunnen 10.0 0 https://www.facebook.com/pages/T%C3%B6pferbrun... ;115090141929327:Park; None Kohren-Sahlis Germany None 04655
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10544 601711063637792 Töpferhaus Arnold Kohren-Sahlis 1.0 42 https://www.facebook.com/Töpferhaus-Arnold-Koh... ;2500:Local Business; Seit 1500 vierzig und acht werden hir Töpfe un... Frohburg Germany Burggasse 2 04654
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10583 1184785731555505 Burggasse 10 Inhaberin:Birgit Steglich 36.0 262 https://www.facebook.com/Terrassencafe / ;197871390225897:Cafe;273819889375819:Restaurant; In der kalten Jahreszeit kannst Du in einem wa... Kohren-Sahlis Germany Burggasse 10 04655
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10569 228625154331420 Firma Konrad Pohl 1.0 0 https://www.facebook.com/pages/Firma-Konrad-Po... ;184395321600410:Cargo & Freight Company; None Kohren-Sahlis Germany Karl-Marx-Straße 191 04654
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10565 244456133015595 Balancehaus 1.0 0 https://www.facebook.com/pages/Balancehaus/244... ;505091123022329:Hotel & Lodging; None Kohren-Sahlis Germany 7 Töpfer Straße 04654
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10568 870478409783656 Kohrener Dachdecker GmbH NaN 0 https://www.facebook.com/pages/Kohrener-Dachde... ;206284062724685:Roofing Service; None Kohren-Sahlis Germany Töpferstr. 6 a 04654
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10576 153940394685072 Gaststätte Kohrener Land 3.0 1 https://www.facebook.com/pages/Gastst%C3%A4tte... ;164049010316507:Gastropub; None Kohren-Sahlis Germany Markt 137 04655
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10551 152653308595280 Heiko Winkler NaN 0 https://www.facebook.com/pages/Heiko-Winkler/1... ;108427109235243:Home Improvement; None Kohren-Sahlis Germany Markt 137 04655
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10543 1025694584130054 Burg Kohren 21.0 12 https://www.facebook.com/pages/Burg-Kohren/155... ;209889829023118:Landmark & Historical Place; Die Burg Kohren, auch Chorun oder Sahlis genan... Kohren-Sahlis Germany None None
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10542 729196057213539 Kohren Castle 2.0 1 https://www.facebook.com/pages/Kohren-Castle/3... ;209889829023118:Landmark & Historical Place; Kohren Castle, also known as Chorun or Sahlis,... Kohren-Sahlis Germany None None
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10548 1304092919676567 Hochzeitsambiente Sunflower NaN 2 https://www.facebook.com/pages/Hochzeitsambien... ;103446129740239:Bridal Shop; None Kohren-Sahlis Germany Markt 139 04654
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10562 421720421691722 Burgblick NaN 0 https://www.facebook.com/pages/Burgblick/42172... ;164243073639257:Hotel; None Kohren-Sahlis Germany Burggasse 51 05431
0 0 POLYGON ((12.84598482873262 51.68354157143052,... 10546 1100292620139210 Heimatforschung-Sachsen NaN 19 https://www.facebook.com/HeimatforschungSachsen/ ;2603:Nonprofit Organization; Das Projekt "Unterstützung des freiwilligen En... Kohren-Sahlis Germany Pestalozzistraße 3 04654

14464 rows × 14 columns

Order Place Categories:

In [235]:
place_cats = pd_place_tuples["PlaceCats"].str.split(';', expand=True, n=2)[1].str.split(':', expand=True, n=2)[1]
cat_count = place_cats.value_counts()
#cat_count.where(cat_count>3)
cat_count[:20]
Out[235]:
City                             755
Local Business                   744
Business Service                 472
Contractor                       323
Local Service                    289
Community Organization           227
Restaurant                       201
Hotel                            177
Shopping & Retail                176
Nonprofit Organization           174
Sports Club                      170
Commercial & Industrial          166
Stadium, Arena & Sports Venue    127
Automotive Repair Shop           125
Sports & Recreation              115
Engineering Service              112
Grocery Store                    100
Hair Salon                        93
Doctor                            82
Public & Government Service       81
Name: 1, dtype: int64

Visualize All Places

In [95]:
# get first category
pd_place_tuples_display = pd_place_tuples.copy()
pd_place_tuples_display['first_cat'] = pd_place_tuples["PlaceCats"].str.split(';', expand=True, n=2)[1].str.split(':', expand=True, n=2)[1]
# limit caption to 50 characters
pd_place_tuples_display['Caption'] = pd_place_tuples_display['Caption'].str[:50]
# scale symbols by sqrt (use np, not math.sqrt!)
pd_place_tuples_display['pt_size'] = 4+np.sqrt(pd_place_tuples_display['Checkins'])/5

Append Count of Unique for main cats to dataframe (used to color main categories)

In [100]:
pd_place_tuples_display['cat_count'] = cat_count.where(cat_count>87).get(pd_place_tuples_display['first_cat']).to_list()
In [101]:
pd_place_tuples_display['main_cats'] = pd_place_tuples_display['first_cat']
In [102]:
pd_place_tuples_display.tail()
# replace all cats that appear less than 1 times by 'other'
pd_place_tuples_display.loc[pd_place_tuples_display['cat_count'].isnull(), 'main_cats'] = 'Other'
In [103]:
pd_place_tuples_display.tail()
Out[103]:
Latitude Longitude PlaceGuid PlaceName Checkins Likes URL PlaceCats Caption City Country Street Zip first_cat pt_size cat_count main_cats
12872 51.503120 12.644450 233347587069968 Elke Veh-Riedel NaN 1 https://www.facebook.com/pages/Elke-Veh-Riedel... ;198516863494646:Nail Salon; None Doberschütz Germany Seitenstraße 34 04838 Nail Salon NaN NaN Other
12873 51.498508 12.643609 1879768312135568 Gemeindeverwaltung NaN 0 https://www.facebook.com/pages/Gemeindeverwalt... ;147714868971098:Public & Government Service; None None None Seitenstr. 3 A 04838 Public & Government Service NaN NaN Other
12874 51.502435 12.642934 164896737592682 Prautzsch Dieter NaN 0 https://www.facebook.com/pages/Prautzsch-Diete... ;199182823425834:Electrician; None Doberschütz None Hauptstr. 49 A 04838 Electrician NaN NaN Other
12875 51.499264 12.643179 1882233518668137 Ruhnow Jürgen Heizungs- und Sanitärinstallatio... 1.0 1 https://www.facebook.com/pages/Ruhnow-J%C3%BCr... ;108427109235243:Home Improvement; None Doberschütz Germany Hauptstraße 15 04838 Home Improvement 4.200000 NaN Other
12876 51.495310 12.643600 575879739201986 Hair-Eck 2.0 52 https://www.facebook.com/Hair-Eck-575879699201... ;169758603141095:Barber Shop;174177802634376:H... Wir schneiden und verändern deine Friseur nach... Mörtitz Germany Stadtweg 7 04838 Barber Shop 4.282843 NaN Other
In [104]:
len(pd_place_tuples_display.main_cats.unique())
Out[104]:
20
In [105]:
fb_place_data_gv = gv.Dataset(
    pd_place_tuples_display, 
    kdims=['Longitude', 'Latitude', 'PlaceGuid',
           'PlaceName', 'Checkins', 'Likes',
           'URL', 'first_cat', 'cat_count', 'main_cats', 'Caption', 'City', 
           'Country', 'Street', 'Zip', 'PlaceCats', 'pt_size'])           
In [106]:
fb_place_data_gv
Out[106]:
:Dataset   [Longitude,Latitude,PlaceGuid,PlaceName,Checkins,Likes,URL,first_cat,cat_count,main_cats,Caption,City,Country,Street,Zip,PlaceCats,pt_size]
In [107]:
%%opts Overlay [legend_position='left']
from holoviews import dim, opts
from math import sqrt
from cartopy import crs as ccrs
#from bokeh.models import Legend

def set_active_tool(plot, element):
    # enable wheel_zoom by default
    plot.state.toolbar.active_scroll = plot.state.tools[0]
    
#from bokeh.models import HoverTool
#hover = HoverTool(tooltips=[("index", "$index")])
#use tools=[hover]]
hv.Overlay(
    gv.tile_sources.EsriImagery.opts(alpha=0.5) * \
    fb_place_data_gv.to(
        gv.Points, 
        kdims=['Longitude', 'Latitude'], 
        vdims=['PlaceName', 'PlaceGuid', 'PlaceCats', 'Checkins', 'Likes', 
               'URL', 'first_cat', 'cat_count', 'main_cats', 'Caption', 'City', 'Country', 
               'Street', 'Zip', 'pt_size'], 
        crs=ccrs.PlateCarree()).opts(tools=['hover'], size='pt_size', color='main_cats', cmap='tab20c')
    ).opts(
        width=1000,
        height=480,
        finalize_hooks=[set_active_tool]
    )
Out[107]:

Visualize Selected Place Categories

Select all cities:

In [34]:
pd_place_city = pd_place_tuples[pd_place_tuples.PlaceCats.str.contains("2404|2401")==True]
fb_city_data_gv = gv.Dataset(
    pd_place_city, 
    kdims=['Longitude', 'Latitude', 'PlaceGuid',
           'PlaceName', 'Checkins', 'Likes',
           'URL','Caption', 'City', 
           'Country', 'Street', 'Zip', 'PlaceCats']) 
print(len(pd_place_city))
1065
In [36]:
pd_place_city.head()
Out[36]:
Latitude Longitude PlaceGuid PlaceName Checkins Likes URL PlaceCats Caption City Country Street Zip
0 51.450000 12.166700 226113570737445 Wiesenena-Rabutz, Sachsen, Germany 13.0 5 https://www.facebook.com/pages/Wiesenena-Rabut... ;2404:City;2401:City; None Wiesenena-Rabutz Germany None None
9 51.314575 12.176322 208966115800610 Altranstädt, Sachsen, Germany 535.0 50 https://www.facebook.com/pages/Altranst%C3%A4d... ;2404:City;2401:City; None Altranstädt Germany None None
10 51.303209 12.168448 218236298187769 Großlehna, Sachsen, Germany 770.0 87 https://www.facebook.com/pages/Gro%C3%9Flehna-... ;2404:City;2401:City; None Großlehna Germany None None
11 51.317465 12.164535 428134407228293 A9 Nähe Leipzig 1600.0 7 https://www.facebook.com/pages/A9-N%C3%A4he-Le... ;2401:City; None Markranstädt Germany None None
60 51.300000 12.166700 200760619965095 Kleinlehna, Sachsen, Germany 3.0 2 https://www.facebook.com/pages/Kleinlehna-Sach... ;2404:City;2401:City; None Kleinlehna Germany None None

Select subsets based on categories

Select Outdoor Recreation:

In [37]:
def make_gv_layer(pd_dataframe):
    '''Create Geoviews layer from Pandas dataframe'''
    gv_layer = gv.Dataset(
        pd_dataframe, 
        kdims=['Longitude', 'Latitude', 'PlaceGuid',
               'PlaceName', 'Checkins', 'Likes',
               'URL','Caption', 'City', 
               'Country', 'Street', 'Zip', 'PlaceCats'])
    return gv_layer
def select_create_gv_layer(id_string_select):
    pd_place_select = pd_place_tuples[pd_place_tuples.PlaceCats.str.contains(id_string_select)]
    fb_select_data_gv = make_gv_layer(
        pd_place_select)
    print(len(fb_select_data_gv))
    return fb_select_data_gv
    
fb_recreation_data_gv = select_create_gv_layer("635235176664335")
21

Select sports category:

In [38]:
fb_sport_data_gv = select_create_gv_layer("186982054657561|189018581118681|964585346994407")
449

Select Landmarks:

In [39]:
fb_landmark_data_gv = select_create_gv_layer("209889829023118")
177

Select Community Service:

In [79]:
fb_community_data_gv = select_create_gv_layer("170968163319233|191523214199822|181053558607965|2235|2603")
366

Select Arts & Entertainment:

In [80]:
fb_arts_data_gv = select_create_gv_layer("133436743388217")
128

Select City Infrastructure:

In [86]:
fb_infrastructure_data_gv = select_create_gv_layer("1713595685546855|1874409019452971|147714868971098|2235|2603|226326230802065|186004504854452")
463
In [113]:
%%opts Points [tools=['hover']]
from cartopy import crs as ccrs
from holoviews import dim, opts


def set_active_tool(plot, element):
    # enable wheel_zoom by default
    plot.state.toolbar.active_scroll = plot.state.tools[0]
    
def make_layer(input_gv_layer, layer_name):
    '''Format Holoviews/Geoviews Layer with vdims and kdims'''
    formatted_layer = input_gv_layer.to(
        gv.Points, 
        kdims=['Longitude', 'Latitude'], 
        vdims=['PlaceName', 'PlaceGuid', 'PlaceCats', 'Checkins', 'Likes', 
               'URL', 'Caption', 'City', 'Country', 
               'Street', 'Zip'], 
        crs=ccrs.PlateCarree(),
        label=layer_name)
    return formatted_layer

# see https://matplotlib.org/api/markers_api.html for markers
hv.Overlay(
    gv.tile_sources.EsriImagery.opts(alpha=0.5) * \
    make_layer(fb_infrastructure_data_gv, 'Infrastructure & Service').opts(size=4+dim('Likes')/1500, color='blue', marker='o') * \
    make_layer(fb_city_data_gv, 'city').opts(size=2+dim('Likes')/5000, color='darkred', marker='o')
    
    ).opts(
        width=1000,
        height=480,
        finalize_hooks=[set_active_tool],
        title='Selected Facebook Places categories in Westsachsen'
    )
Out[113]:
In [117]:
points1 = make_layer(fb_infrastructure_data_gv, 'Infrastructure & Service')
points2 = make_layer(fb_city_data_gv, 'city')

Select subsets based on categories (One Layer)

In [12]:
def update_selcat_col(df, id_string_select, place_cat_name):
    #df[df.PlaceCats.str.contains(id_string_select)].SelCat = place_cat_name
    df.loc[df.PlaceCats.str.contains(id_string_select), 'SelCat'] = place_cat_name
    #return df

pd_places_display_cat = pd_place_tuples.copy()
pd_places_display_cat['Caption'] = pd_place_tuples['Caption'].str[:256]
pd_places_display_cat["SelCat"] = None
update_selcat_col(pd_places_display_cat, "186982054657561|189018581118681|964585346994407", "Sport")
update_selcat_col(pd_places_display_cat, "1713595685546855|1874409019452971|147714868971098|2235|2603|226326230802065|186004504854452", "Infrastructure & Service")
update_selcat_col(pd_places_display_cat, "133436743388217", "Arts & Entertainment")
update_selcat_col(pd_places_display_cat, "170968163319233|191523214199822|181053558607965|2235|2603", "Community Service")
update_selcat_col(pd_places_display_cat, "635235176664335", "Recreation")
update_selcat_col(pd_places_display_cat, "209889829023118", "Landmark")
update_selcat_col(pd_places_display_cat, "2404|2401", "City")

# replace all other labels
pd_places_display_cat.SelCat.fillna("Other", inplace=True)
In [13]:
pd_places_display_cat.head()
Out[13]:
Latitude Longitude PlaceGuid PlaceName Checkins Likes URL PlaceCats Caption City Country Street Zip SelCat
0 51.450000 12.16670 226113570737445 Wiesenena-Rabutz, Sachsen, Germany 13.0 5 https://www.facebook.com/pages/Wiesenena-Rabut... ;2404:City;2401:City; None Wiesenena-Rabutz Germany None None City
1 51.444990 12.16988 1231839476954885 Technisch-Ökologisches Projektzentrum Rabutz NaN 2 https://www.facebook.com/projektzentrum.rabutz/ ;297544187300691:Science, Technology & Enginee... Im Technisch-Ökologischen Projektzentrum Rabut... Wiesenena-Rabutz Germany Begeritzer Weg 1 04509 Other
2 51.444990 12.16988 1514362865299432 Verein der Ingenieure, Techniker u. Wirtschaf... NaN 0 https://www.facebook.com/pages/Verein-der-Inge... ;152880021441864:Community Organization; None Wiedemar Germany Bageritzer Weg 1 04509 Other
3 51.444761 12.16995 304703456223188 Rabutz 43.0 1 https://www.facebook.com/pages/Rabutz/30470343... ;191478144212980:Dance & Night Club; None Wiedemar Germany None 04509 Other
4 51.382500 12.17810 368036490011425 Neue Luppe 73.0 2 https://www.facebook.com/pages/Neue-Luppe/2924... ;209889829023118:Landmark & Historical Place; Die Neue Luppe ist ein künstlicher Nebenarm de... Schkeuditz Germany None None Landmark
In [16]:
%%opts Points [tools=['hover']] Overlay [title_format="All Facebook Places and selected categories in Westsachsen (Public Facebook Place Graph API)"]
%%output filename="westsachsen_fb_places"
from holoviews.operation.stats import bivariate_kde
from cartopy import crs as ccrs
from holoviews import dim, opts
hv.notebook_extension('bokeh')

def set_active_tool(plot, element):
    # enable wheel_zoom by default
    plot.state.toolbar.active_scroll = plot.state.tools[0]

gv_layer = gv.Dataset(
        pd_places_display_cat, 
        kdims=['Longitude', 'Latitude', 'PlaceGuid',
               'PlaceName', 'Checkins', 'Likes',
               'URL','Caption', 'City', 
               'Country', 'Street', 'Zip', 'PlaceCats', 'SelCat'])
# colormaps: http://build.holoviews.org/user_guide/Colormaps.html
hv.Overlay(
    gv.tile_sources.EsriImagery.opts(alpha=0.5) * \
    gv.Polygons(kreis_polys).opts(fill_alpha=0) * \
    #bivariate_kde(gv_layer, bandwidth=0.1).opts(show_legend=False, cmap='Blues', alpha=0.7) * \
    gv_layer.to(
            gv.Points, 
            kdims=['Longitude', 'Latitude'], 
            vdims=['PlaceName', 'PlaceGuid', 'PlaceCats', 'Checkins', 'Likes', 
                   'URL', 'Caption', 'City', 'Country', 
                   'Street', 'Zip', 'SelCat'], 
            crs=ccrs.PlateCarree()).opts(size=2.5+dim('Likes')/7000, 
                                         color='SelCat', cmap='Category20'),
            
        ).opts(
    #responsive=True,
    finalize_hooks=[set_active_tool],
    width=1000,
    height=480,
    )
Out[16]:
In [ ]: