def test_validation():
    h = '8a28308280fffff'  # invalid hex

    with pytest.raises(H3CellError):
        h3.h3_get_base_cell(h)

    with pytest.raises(H3CellError):
        h3.h3_get_resolution(h)

    with pytest.raises(H3CellError):
        h3.h3_to_parent(h, 9)

    with pytest.raises(H3CellError):
        h3.h3_distance(h, h)

    with pytest.raises(H3CellError):
        h3.k_ring(h, 1)

    with pytest.raises(H3CellError):
        h3.hex_ring(h, 1)

    with pytest.raises(H3CellError):
        h3.h3_to_children(h, 11)

    with pytest.raises(H3CellError):
        h3.compact({h})

    with pytest.raises(H3CellError):
        h3.uncompact({h}, 10)
Example #2
0
def test_h3_set_to_multi_polygon_2k_ring():
    h = '8930062838bffff'
    hexes = h3.k_ring(h, 2)
    # multi_polygon
    mp = h3.h3_set_to_multi_polygon(hexes)

    assert len(mp) == 1  # polygon count matches expected
    assert len(mp[0]) == 1  # loop count matches expected
    assert len(mp[0][0]) == 6 * (2 * 2 + 1)  # coord count matches expected

    hexes2 = {
        '89300628393ffff', '89300628383ffff', '89300628397ffff',
        '89300628067ffff', '89300628387ffff', '893006283bbffff',
        '89300628313ffff', '893006283cfffff', '89300628303ffff',
        '89300628317ffff', '8930062839bffff', h,
        '8930062806fffff', '8930062838fffff', '893006283d3ffff',
        '893006283c3ffff', '8930062831bffff', '893006283d7ffff',
        '893006283c7ffff'
    }

    mp2 = h3.h3_set_to_multi_polygon(hexes2)

    assert len(mp2) == 1  # polygon count matches expected
    assert len(mp2[0]) == 1  # loop count matches expected
    assert len(mp2[0][0]) == 6 * (2 * 2 + 1)  # coord count matches expected

    hexes3 = list(h3.k_ring(h, 6))
    hexes3.sort()
    mp3 = h3.h3_set_to_multi_polygon(hexes3)

    assert len(mp3[0]) == 1  # loop count matches expected
Example #3
0
def test_hex_ring():
    h = '8928308280fffff'
    out = h3.hex_ring(h, 1)
    expected = {
        '8928308280bffff',
        '89283082807ffff',
        '89283082877ffff',
        '89283082803ffff',
        '89283082873ffff',
        '8928308283bffff',
    }

    assert out == expected
    assert out == h3.k_ring(h, 1) - h3.k_ring(h, 0)
Example #4
0
def get_exposure(events, attack_type, p, freq):
    """
    """

    cells = []
    for country in p.countries:
        cells += list(json_to_h3(country, p.h3_level))

    cells_coord = np.array(
        [[h3.h3_to_geo(c)[0], h3.h3_to_geo(c)[1]] for c in cells],
        dtype='float64')
    events_of_type = events[events["type"] == attack_type]
    events_coord = events_of_type[["latitude",
                                   "longitude"]].to_numpy(dtype='float64')

    events_h3 = events_of_type["h3"].to_numpy()
    rings = np.array([list(h3.k_ring(str(cell), 2)) for cell in cells],
                     dtype='object')
    is_in_ring = np.array([np.isin(events_h3, ring) for ring in rings])

    date_range = pd.date_range(p.startdate, p.enddate, freq=freq)
    date_range = ((date_range - pd.Timestamp("1970-01-01")) //
                  pd.Timedelta("1s")).to_numpy()

    events_dates = ((events["date_start"] - pd.Timestamp("1970-01-01")) //
                    pd.Timedelta("1s")).to_numpy()

    distances = calc_distances(cells_coord, events_coord, is_in_ring)

    g = get_exposure_raw(events_dates, is_in_ring, distances, date_range)

    return pd.DataFrame(g,
                        index=cells,
                        columns=pd.to_datetime(date_range, unit="s"))
Example #5
0
def test_k_ring2():
    h = '8928308280fffff'
    out = h3.k_ring(h, 2)

    assert len(out) == 1 + 6 + 12

    expected = {
        '89283082813ffff',
        '89283082817ffff',
        '8928308281bffff',
        '89283082863ffff',
        '89283082823ffff',
        '89283082873ffff',
        '89283082877ffff',
        h,
        '8928308287bffff',
        '89283082833ffff',
        '8928308282bffff',
        '8928308283bffff',
        '89283082857ffff',
        '892830828abffff',
        '89283082847ffff',
        '89283082867ffff',
        '89283082803ffff',
        '89283082807ffff',
        '8928308280bffff',
    }

    assert out == expected
Example #6
0
def cell_exposure_at_t(events, attack_type, origin, date, distances):
    """
    Calcule l'exposition à date de la cellule origin aux events de type attack_type.
    """

    ring = h3.k_ring(origin, 2)
    c_events = events.loc[(events['h3'].isin(ring)) & (
        events['type'] == attack_type
    )].copy(
    )  # On copie https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

    # Calculate wa

    def calc_event_age(row):
        return date - row['date_start']

    def calc_wa(row):
        if row['age'].days < 0:
            # L'évènement n'a pas encore eu lieu, on ne le prend pas en compte
            return 0

        return wa(row['age'].days)

    c_events['age'] = c_events.apply(calc_event_age,
                                     axis=1,
                                     result_type='reduce')
    c_events['wa'] = c_events.apply(calc_wa, axis=1, result_type='reduce')

    wd_df = distances[origin].loc[(events['h3'].isin(ring))
                                  & (events['type'] == attack_type)]

    return np.dot(wd_df, c_events['wa'])
Example #7
0
def calc_distances(cells, events):
    distances = pd.DataFrame(index=events.index)
    distances['h3'] = events['h3'].copy()

    def calc_event_distance(origin, row):
        cell_lat, cell_lng = h3.h3_to_geo(origin)
        ev_lat, ev_lng = row['latitude'], row['longitude']
        return harvesine(cell_lng, cell_lat, ev_lng, ev_lat)

    for cell in cells:
        ring = h3.k_ring(cell, 2)
        c_events = events.loc[(events['h3'].isin(ring))].copy()

        c_events['distance'] = c_events.apply(
            lambda row: calc_event_distance(cell, row),
            axis=1,
            result_type='reduce')
        c_events['wd'] = c_events.apply(lambda row: wd(row['distance']),
                                        axis=1,
                                        result_type='reduce')

        #distances[cell] = c_events['distance']

        distances[cell] = c_events['wd']

    return distances.drop('h3', axis=1)
Example #8
0
def test_many_hex_ranges2():
    hexes = h3.k_ring('8928308280fffff', 5)
    out = h3.hex_ranges(hexes, 5)
    assert len(out) == 91

    hexes = out['8928308280fffff']

    assert [len(x) for x in hexes] == [1, 6, 12, 18, 24, 30]
Example #9
0
def test_many_hex_ranges():
    hexes = h3.k_ring('8928308280fffff', 2)
    out = h3.hex_ranges(hexes, 2)

    assert len(out) == 19

    hexes = out['8928308280fffff']
    assert [len(x) for x in hexes] == [1, 6, 12]
def test5():
    expected = {
        '89283082873ffff', '89283082877ffff', '8928308283bffff',
        '89283082807ffff', '8928308280bffff', '8928308280fffff',
        '89283082803ffff'
    }

    out = h3.k_ring('8928308280fffff', 1)
    assert out == expected
Example #11
0
def test_h3_set_to_multi_polygon():
    h = '8928308280fffff'
    hexes = h3.k_ring(h, 1)

    mpoly = h3.h3_set_to_multi_polygon(hexes)

    out = h3.polyfill_polygon(mpoly[0][0], 9, holes=None, lnglat_order=False)

    assert out == hexes
Example #12
0
def test_hex_ring2():
    h = '8928308280fffff'
    out = h3.hex_ring(h, 2)

    expected = {
        '89283082813ffff',
        '89283082817ffff',
        '8928308281bffff',
        '89283082863ffff',
        '89283082823ffff',
        '8928308287bffff',
        '89283082833ffff',
        '8928308282bffff',
        '89283082857ffff',
        '892830828abffff',
        '89283082847ffff',
        '89283082867ffff',
    }

    assert out == expected
    assert out == h3.k_ring(h, 2) - h3.k_ring(h, 1)
Example #13
0
    def fit_model(self,
                  simulation_file: str,
                  k_ring_factor: float = 0.4,
                  k_k_ring: float = 1):
        sim_data = pd.read_csv(f'simulations/{simulation_file}',
                               parse_dates=['timestamp'])

        # regular_sampling for time series analysis
        sim_data_reg = pd.DataFrame(
            sim_data.groupby(['hex']).resample('5T',
                                               on='timestamp')['lat'].count())
        sim_data_reg.reset_index(inplace=True)
        sim_data_reg.rename(columns={'lat': 'dem'}, inplace=True)

        #k-ring "convolution"
        for hex_id in sim_data_reg.hex.unique():
            # data from hexagon
            hex_data = sim_data_reg[sim_data_reg.hex == hex_id].copy()

            # data from kring
            kring_hex_list = [
                hex for hex in h3.k_ring(hex_id, k=k_k_ring) if hex != hex_id
            ]
            kring_data = sim_data_reg[sim_data_reg.hex.isin(kring_hex_list)]
            kring_data_agg = kring_data.groupby('timestamp',
                                                as_index=False).dem.sum()
            kring_data_agg.rename(columns={'dem': 'kring_dem'}, inplace=True)

            # merge both
            hex_data_kring = pd.merge(left=hex_data,
                                      right=kring_data_agg,
                                      how='outer',
                                      on='timestamp')
            hex_data_kring.fillna({
                'hex': hex_id,
                'dem': 0,
                'kring_dem': 0
            },
                                  inplace=True)

            # build ponderate demand
            hex_data_kring['y'] = hex_data_kring['dem'] * (
                1 - k_ring_factor) + (hex_data_kring['kring_dem'] *
                                      k_ring_factor)
            # fit hex model
            hex_model = self.fit_hex_model(hex_data_kring)

            self.model_dict[hex_id] = hex_model

        self.is_fitted = True
Example #14
0
def test_k_ring_pentagon():
    h = '821c07fffffffff'  # a pentagon cell
    out = h3.k_ring(h, 1)

    assert len(out) == 1 + 5

    expected = {
        '821c2ffffffffff',
        '821c27fffffffff',
        h,
        '821c17fffffffff',
        '821c1ffffffffff',
        '821c37fffffffff',
    }

    assert out == expected
Example #15
0
def cell_exposure_at_t_with_cas(events, attack_type, origin, date, distances):
    """
    Calcule l'exposition à date de la cellule origin aux events de type attack_type
    en prenant en compte le nombre de victimes ("cas" = casualties).
    """

    ring = h3.k_ring(origin, 2)
    c_events = events.loc[(events['h3'].isin(ring)) & (
        events['type'] == attack_type
    )].copy(
    )  # On copie https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

    # Calculate wa

    def calc_event_age(row):
        return date - row['date_start']

    def calc_wa(row):
        if row['age'].days < 0:
            # L'évènement n'a pas encore eu lieu, on ne le prend pas en compte
            return 0

        return wa(row['age'].days)

    c_events['age'] = c_events.apply(calc_event_age,
                                     axis=1,
                                     result_type='reduce')
    c_events['wa'] = c_events.apply(calc_wa, axis=1, result_type='reduce')

    # Fetch wd

    wd_df = distances[origin].loc[(events['h3'].isin(ring))
                                  & (events['type'] == attack_type)]

    # Calculate wcas

    def calc_wcas(row):
        # On regarde le nombre moyen de victimes sur les trois derniers mois
        recent_events = c_events.loc[(c_events['age'].dt.days >= 0)
                                     & (c_events['age'].dt.days <= 90)]
        mean = recent_events['fatalities'].mean()
        return wcas(mean, row['fatalities'])

    c_events['wcas'] = c_events.apply(calc_wcas, axis=1, result_type='reduce')
    c_events.fillna(c_events['wcas'].mean(), inplace=True)

    return np.dot(wd_df, c_events['wa'] * c_events['wcas'])
Example #16
0
def test_k_ring():
    h = '8928308280fffff'
    out = h3.k_ring(h, 1)

    assert len(out) == 1 + 6

    expected = {
        '8928308280bffff',
        '89283082807ffff',
        '89283082877ffff',
        h,
        '89283082803ffff',
        '89283082873ffff',
        '8928308283bffff',
    }

    assert out == expected
Example #17
0
    def filter_h3d_around(cls, lat, lon, res, k_distance=1, queryset=None):
        """
        Filter all instances with the same h3 cell, or in cells around it

        :param lat: latitude
        :param lon: longitude
        :param res: resolution (level, depth) of the index where to find
        :param k_distance: max distance in hexagon cells from the cell containing the start point
            0 for the containing cell only,
            1 for the containing cell and cells immediately around,
            etc.
        :param queryset: the queryset to search in, None means cls.objects.all()
        """
        cells = h3.compact(h3.k_ring(h3.geo_to_h3(lat, lon, res), k_distance))
        d_cells = [h3d.h3s_to_h3d(c) for c in cells]
        filters = [models.Q(h3d__range=h3d.h3d_range(c)) for c in d_cells]
        if not queryset:
            queryset = cls.objects.all()
        return queryset.filter(functools.reduce(operator.or_, filters))
def test_k_ring_distance():
    with pytest.raises(H3DistanceError):
        h3.k_ring('8928308280fffff', -10)
def create_smother_demand_mapper(
    hex_cluster: List[str],
    day_intervals: [str, dict] = 'default',
    hex_with_high_demand: [str, dict] = 'default',
    cat_lambda_map: [str, dict] = 'default',
) -> Dict[Tuple[str, int], float]:
    if day_intervals == 'default':
        day_intervals_dict = {
            'morning': [7, 10],
            'mid_morning': [11, 15],
            'afternoon': [16, 18],
            'night': [19, 22]
        }
    else:
        day_intervals_dict = deepcopy(day_intervals)
    if hex_with_high_demand == 'default':
        hex_with_high_demand_dict = {
            'morning': 1,
            'mid_morning': 3,
            'afternoon': 1,
            'night': 3
        }
    else:
        hex_with_high_demand_dict = deepcopy(hex_with_high_demand)
    if cat_lambda_map == 'default':
        cat_lambda_map_dict = {
            'low_demand': 900,  # time in minutes
            'mid_demand': 360,
            'high_demand': 120,
        }
    else:
        cat_lambda_map_dict = deepcopy(cat_lambda_map)

    demand_map_sim = {}
    for day_window in day_intervals_dict.keys():
        # select n peaks
        hex_peak_list = random.sample(hex_cluster,
                                      hex_with_high_demand_dict[day_window])
        # expand n peaks to k ring = 2
        hex_high_demand = set(hex_peak_list)
        for high_peak_hex in hex_peak_list:
            neighborhood = h3.k_ring(high_peak_hex, k=1)
            hex_high_demand.update(neighborhood)
        # expand to mid_demand
        hex_mid_demand = set()
        for high_peak_hex in hex_high_demand:
            neighborhood = h3.k_ring(high_peak_hex, k=2)
            outer_hex = set(neighborhood).difference(hex_high_demand)
            hex_mid_demand.update(outer_hex)

        # iterate over hours
        window_low_bound = day_intervals_dict[day_window][0]
        window_up_bound = day_intervals_dict[day_window][1] + 1

        for hour in range(window_low_bound, window_up_bound):
            for hex in hex_cluster:
                if hex in hex_high_demand:
                    demand_map_sim[tuple(
                        [hex, hour])] = cat_lambda_map_dict['high_demand']
                elif hex in hex_mid_demand:
                    demand_map_sim[tuple(
                        [hex, hour])] = cat_lambda_map_dict['mid_demand']
                else:
                    demand_map_sim[tuple(
                        [hex, hour])] = cat_lambda_map_dict['low_demand']
    return demand_map_sim
Example #20
0
def poc_polar(hotspot, chals):

    H = Hotspots()
    haddr = hotspot['address']
    hlat, hlng = hotspot['lat'], hotspot['lng']
    hname = hotspot['name']

    if os.path.exists(hname):
        files = glob(hname + '\\*')
        for file in files:
            os.remove(file)
    else:
        os.mkdir(hname)

    wl = {}  #witnesslist
    rl = {
    }  #received list of hotspots(hotspot of intereset has been witness to these or received from them)
    c = 299792458

    for chal in chals:  # loop through challenges

        for p in chal['path']:  #path?

            if p['challengee'] == haddr:  # handles cases where hotspot of interest is transmitting
                for w in p[
                        'witnesses']:  #loop through witnesses so we can get rssi at each location challenge received
                    #print('witness',w)
                    lat = H.get_hotspot_by_addr(w['gateway'])['lat']
                    lng = H.get_hotspot_by_addr(w['gateway'])['lng']
                    name = H.get_hotspot_by_addr(w['gateway'])['name']
                    dist_km, heading = utils.haversine_km(hlat,
                                                          hlng,
                                                          lat,
                                                          lng,
                                                          return_heading=True)

                    fspl = 20 * log10((dist_km + 0.01) * 1000) + 20 * log10(
                        915000000) + 20 * log10(4 * pi / c) - 27

                    try:
                        wl[w['gateway']]['lat'] = lat
                        wl[w['gateway']]['lng'] = lng
                        wl[w['gateway']]['rssi'].append(w['signal'])
                    except KeyError:
                        wl[w['gateway']] = {
                            'rssi': [
                                w['signal'],
                            ],
                            'dist_km': dist_km,
                            'heading': heading,
                            'fspl': fspl,
                            'lat': lat,
                            'lng': lng,
                            'name': name
                        }
            else:  # hotspot of interest is not transmitting but may be a witness
                challengee = p['challengee']
                name = H.get_hotspot_by_addr(challengee)['name']
                for w in p['witnesses']:
                    if w['gateway'] != haddr:
                        continue
                    #print('transmitter ', name)
                    #print('witness ', H.get_hotspot_by_addr(w['gateway'])['name']) # hotspot of interest was a witness

                    lat = H.get_hotspot_by_addr(challengee)['lat']
                    lng = H.get_hotspot_by_addr(challengee)['lng']
                    #name=H.get_hotspot_by_addr(w['gateway'])['name']
                    dist_km, heading = utils.haversine_km(hlat,
                                                          hlng,
                                                          lat,
                                                          lng,
                                                          return_heading=True)

                    fspl = 20 * log10((dist_km + 0.01) * 1000) + 20 * log10(
                        915000000) + 20 * log10(4 * pi / c) - 27

                    try:
                        rl[challengee]['lat'] = lat
                        rl[challengee]['lng'] = lng
                        rl[challengee]['rssi'].append(w['signal'])
                    except KeyError:
                        rl[challengee] = {
                            'rssi': [
                                w['signal'],
                            ],
                            'dist_km': dist_km,
                            'heading': heading,
                            'fspl': fspl,
                            'lat': lat,
                            'lng': lng,
                            'name': name
                        }

    #print('rl:',rl)
    ratios = [1.0] * 16
    rratios = [1.0] * 16
    N = len(ratios) - 1
    angles = []
    rangles = []
    #angles = [n / float(N) *2 *pi for n in range(N+1)]
    angles = list(np.arange(0.0, 2 * np.pi + (2 * np.pi / N), 2 * np.pi / N))
    rangles = list(np.arange(0.0, 2 * np.pi + (2 * np.pi / N), 2 * np.pi / N))
    #print(angles,len(angles))
    #print(ratios,len(ratios))

    markers = []
    encoded = {}
    rencoded = {}
    for w in wl:  #for witness in witnesslist
        #print(wl[w])
        mean_rssi = sum(wl[w]['rssi']) / len(wl[w]['rssi'])
        ratio = wl[w]['fspl'] / mean_rssi * (-1)
        if ratio > 3.0:
            ratio = 3.0
        elif ratio < -3.0:
            ratio = -3.0
        ratios.append(ratio)
        angles.append(wl[w]['heading'] * pi / 180)

        #markers.append(folium.Marker([wl[w]['lat'],wl[w]['lng']],popup=wl[w]['name']))
        markers.append([[wl[w]['lat'], wl[w]['lng']], wl[w]['name']])

        # the histogram of the data
        #unique=set(wl[w]['rssi'])
        #num_unique=len(unique)

        n, bins, patches = plt.hist(
            wl[w]['rssi'], 10)  #, density=True, facecolor='g', alpha=0.75,)
        plt.xlabel('RSSI(dB)')
        plt.ylabel('Count(Number of Packets)')
        wit = str(wl[w]['name'])
        plt.title('Packets from ' + hname + ' measured at ' + wit)
        #plt.text(60, .025, r'$\mu=100,\ \sigma=15$')
        #plt.xlim(40, 160)
        #plt.ylim(0, 0.03)
        plt.grid(True)
        #plt.show()
        strFile = str(wl[w]['name']) + '.jpg'
        strWitness = str(wl[w]['name'])

        if os.path.isfile(strFile):
            #print('remove')
            os.remove(strFile)  # Opt.: os.system("rm "+strFile)
        plt.savefig(hname + '//' + strFile)
        encoded[strWitness] = base64.b64encode(
            open(hname + '//' + strFile, 'rb').read())
        plt.close()

    for w in rl:  #for witness in witnesslist
        #print(rl[w])
        mean_rssi = sum(rl[w]['rssi']) / len(rl[w]['rssi'])
        rratio = rl[w]['fspl'] / mean_rssi * (-1)
        if rratio > 3.0:
            rratio = 3.0
        elif rratio < -3.0:
            rratio = -3.0
        rratios.append(rratio)
        rangles.append(rl[w]['heading'] * pi / 180)

        #markers.append([[wl[w]['lat'],wl[w]['lng']],wl[w]['name']])

        n, bins, patches = plt.hist(
            rl[w]['rssi'], 10)  #, density=True, facecolor='g', alpha=0.75,)
        plt.xlabel('RSSI(dB)')
        plt.ylabel('Count(Number of Packets)')
        wit = str(rl[w]['name'])
        plt.title('Packets from ' + wit + ' measured at ' + hname)

        plt.grid(True)
        #plt.show()
        strFile = 'rrr' + str(rl[w]['name']) + '.jpg'
        strWitness = str(rl[w]['name'])

        if os.path.isfile(strFile):
            #print('remove')
            os.remove(strFile)  # Opt.: os.system("rm "+strFile)
        plt.savefig(hname + '//' + strFile)
        rencoded[strWitness] = base64.b64encode(
            open(hname + '//' + strFile, 'rb').read())
        plt.close()

    # create polar chart
    angles, ratios = zip(*sorted(zip(angles, ratios)))
    rangles, rratios = zip(*sorted(zip(rangles, rratios)))
    angles, ratios = (list(t) for t in zip(*sorted(zip(angles, ratios))))
    rangles, rratios = (list(t) for t in zip(*sorted(zip(rangles, rratios))))

    fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
    ax.set_theta_zero_location("N")
    ax.set_theta_direction(-1)
    #ax.set_rmax(3)
    #ax.set_rmin(-3)
    ax.set_ylim(-3, 3)
    ax.plot(angles,
            ratios,
            marker='^',
            linestyle='solid',
            color='tomato',
            linewidth=2,
            markersize=5,
            label='Transmitting')  #markerfacecolor='m', markeredgecolor='k',
    ax.plot(rangles,
            rratios,
            marker='v',
            linestyle='solid',
            color='dodgerblue',
            linewidth=1,
            markersize=5,
            label='Receiving')  #, markerfacecolor='m', markeredgecolor='k'
    ax.legend(bbox_to_anchor=(0, 1),
              fancybox=True,
              framealpha=0,
              loc="lower left",
              facecolor='#000000')
    plt.xlabel('FSPL/RSSI')

    plt.savefig(hname + '//' + hname + '.png', transparent=True)
    #plt.show()

    # add polar chart as a custom icon in map
    m = folium.Map([hlat, hlng],
                   tiles='stamentoner',
                   zoom_start=18,
                   control_scale=True,
                   max_zoom=20)
    polargroup = folium.FeatureGroup(name='Polar Plot')

    icon = folium.features.CustomIcon(icon_image=hname + '//' +
                                      hotspot['name'] + '.png',
                                      icon_size=(640, 480))
    marker = folium.Marker([hlat, hlng], popup=hotspot['name'], icon=icon)
    polargroup.add_child(marker)

    # add witness markers
    hsgroup = folium.FeatureGroup(name='Witnesses')
    hsgroup.add_child(folium.Marker([hlat, hlng], popup=hotspot['name']))
    # add the witness markers
    for marker in markers:
        #html = '<img src="data:image/jpg;base64,{}">'.format
        html = '<p><img src="data:image/jpg;base64,{}" alt="" width=640 height=480 /></p> \
               <p><img src="data:image/jpg;base64,{}" alt="" width=640 height=480 /></p>'.format

        #print('marker',marker)
        try:
            iframe = IFrame(html(encoded[marker[1]].decode('UTF-8'),
                                 rencoded[marker[1]].decode('UTF-8')),
                            width=640 + 25,
                            height=960 + 40)
            popup = folium.Popup(iframe, max_width=2650)
            mark = folium.Marker(marker[0], popup=popup)
            hsgroup.add_child(mark)
        except KeyError:  # this means this witness never heard from us so there is no marker for it
            pass  # not sure where to put the receive packet histogram so just ignore for now

    radius = 0.01
    center = Point(hlat, hlng)
    circle = center.buffer(radius)  # Degrees Radius
    gjcircle = shapely.geometry.mapping(circle)
    circle = center.buffer(radius * 25)  # Degrees Radius
    gjcircle8 = shapely.geometry.mapping(circle)

    dcgroup = folium.FeatureGroup(name='Distance Circles', show=False)
    radius = 0.01
    center = Point(hlat, hlng)
    circle = center.buffer(radius)  # Degrees Radius
    gjcircle = shapely.geometry.mapping(circle)
    circle = gjcircle['coordinates'][0]
    my_Circle = folium.Circle(location=[hlat, hlng],
                              radius=300,
                              popup='300m',
                              tooltip='300m')
    dcgroup.add_child(my_Circle)
    my_Circle = folium.Circle(location=[hlat, hlng],
                              radius=1000,
                              popup='1km',
                              tooltip='1km')
    dcgroup.add_child(my_Circle)
    my_Circle = folium.Circle(location=[hlat, hlng],
                              radius=2000,
                              popup='2km',
                              tooltip='2km')
    dcgroup.add_child(my_Circle)
    my_Circle = folium.Circle(location=[hlat, hlng],
                              radius=3000,
                              name='circles',
                              popup='3km',
                              tooltip='3km')
    dcgroup.add_child(my_Circle)
    my_Circle = folium.Circle(location=[hlat, hlng],
                              radius=4000,
                              popup='4km',
                              tooltip='4km')
    dcgroup.add_child(my_Circle)
    my_Circle = folium.Circle(location=[hlat, hlng],
                              radius=5000,
                              popup='5km',
                              tooltip='5km')
    dcgroup.add_child(my_Circle)
    my_Circle = folium.Circle(location=[hlat, hlng],
                              radius=10000,
                              popup='10km',
                              tooltip='10km')
    dcgroup.add_child(my_Circle)

    h3colorgroup = folium.FeatureGroup(name='h3 Hexagon Grid Color Fill',
                                       show=False)
    style = {'fillColor': '#f5f5f5', 'lineColor': '#ffffbf'}
    #polygon = folium.GeoJson(gjson, style_function = lambda x: style).add_to(m)

    h3group = folium.FeatureGroup(name='h3 r11 Hex Grid', show=False)
    h3namegroup = folium.FeatureGroup(name='h3 r11 Hex Grid Names', show=False)
    h3fillgroup = folium.FeatureGroup(name='h3 r11 Hex Grid Color Fill',
                                      show=True)
    h3r8namegroup = folium.FeatureGroup(name='h3 r8 Hex Grid Names',
                                        show=False)
    h3r8group = folium.FeatureGroup(name='h3 r8 Hex Grid', show=False)
    hexagons = list(h3.polyfill(gjcircle, 11))
    hexagons8 = list(h3.polyfill(gjcircle8, 8))
    polylines = []

    lat = []
    lng = []
    i = 0
    #print('hexagon',hexagons[0])
    #print(dir(h3))
    home_hex = h3.geo_to_h3(hlat, hlng, 11)
    a = h3.k_ring(home_hex, 7)
    for h in a:
        gjhex = h3.h3_to_geo_boundary(h, geo_json=True)
        gjhex = geometry.Polygon(gjhex)
        mean_rsrp = -60
        folium.GeoJson(
            gjhex,
            style_function=lambda x, mean_rsrp=mean_rsrp: {
                'fillColor': map_color_rsrp(mean_rsrp),
                'color': map_color_rsrp(mean_rsrp),
                'weight': 1,
                'fillOpacity': 0.5
            },
            #tooltip='tooltip'
        ).add_to(h3fillgroup)

    for hex in hexagons:
        p2 = h3.h3_to_geo(hex)
        #p2 = [45.3311, -121.7113]
        folium.Marker(
            p2,
            name='hex_names',
            icon=DivIcon(
                #icon_size=(150,36),
                #icon_anchor=(35,-45),
                icon_anchor=(35, 0),
                html='<div style="font-size: 6pt; color : black">' + str(hex) +
                '</div>',
            )).add_to(h3namegroup)
        #m.add_child(folium.CircleMarker(p2, radius=15))

        polygons = h3.h3_set_to_multi_polygon([hex], geo_json=False)
        # flatten polygons into loops.
        outlines = [loop for polygon in polygons for loop in polygon]
        polyline = [outline + [outline[0]] for outline in outlines][0]
        lat.extend(map(lambda v: v[0], polyline))
        lng.extend(map(lambda v: v[1], polyline))
        polylines.append(polyline)

    for polyline in polylines:
        my_PolyLine = folium.PolyLine(locations=polyline,
                                      weight=1,
                                      color='blue')
        h3group.add_child(my_PolyLine)

    polylines = []

    lat = []
    lng = []
    #polylines8 = []
    for hex in hexagons8:
        p2 = h3.h3_to_geo(hex)
        folium.Marker(
            p2,
            name='hex_names',
            icon=DivIcon(
                #icon_size=(150,36),
                #icon_anchor=(35,-45),
                icon_anchor=(35, 0),
                html='<div style="font-size: 8pt; color : black">' + str(hex) +
                '</div>',
            )).add_to(h3r8namegroup)

        polygons = h3.h3_set_to_multi_polygon([hex], geo_json=False)
        # flatten polygons into loops.
        outlines = [loop for polygon in polygons for loop in polygon]
        polyline = [outline + [outline[0]] for outline in outlines][0]
        lat.extend(map(lambda v: v[0], polyline))
        lng.extend(map(lambda v: v[1], polyline))
        polylines.append(polyline)

    for polyline in polylines:
        my_PolyLine = folium.PolyLine(locations=polyline,
                                      weight=1,
                                      color='blue')
        h3r8group.add_child(my_PolyLine)

    # add possible tiles
    folium.TileLayer('cartodbpositron').add_to(m)
    folium.TileLayer('cartodbdark_matter').add_to(m)
    folium.TileLayer('openstreetmap').add_to(m)
    folium.TileLayer('Mapbox Bright').add_to(m)
    #folium.TileLayer('stamentoner').add_to(m)

    # add markers layer
    #marker_cluster = MarkerCluster().add_to(m)

    polargroup.add_to(m)  #polar plot
    hsgroup.add_to(m)  #hotspots
    dcgroup.add_to(m)  #distance circles
    h3group.add_to(m)
    h3namegroup.add_to(m)
    h3fillgroup.add_to(m)
    m.keep_in_front(h3group)
    h3r8group.add_to(m)
    h3r8namegroup.add_to(m)

    # add the layer control
    folium.LayerControl(collapsed=False).add_to(m)
    m.save(hname + '//' + hname + '_map.html')