Skip to content

Social venue

SocialVenue

Bases: Group

Source code in june/groups/leisure/social_venue.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class SocialVenue(Group):
    """ """
    max_size = np.inf

    def __init__(self, area=None):
        super().__init__()  # Pass disease_config to the parent class
        self.area = area
        # Use 0 as domain_id in non-MPI mode
        self.domain_id = 0 if not mpi_available else mpi_rank


    def add(self, person, activity="leisure"):
        """

        Args:
            person: 
            activity: (Default value = "leisure")

        """
        self.subgroups[0].append(person)
        setattr(person.subgroups, activity, self.subgroups[0])

    @property
    def super_area(self):
        """ """
        return self.area.super_area

    @property
    def get_coordinates(self):
        """ """
        if self.area is None:
            return
        else:
            return self.area.coordinates

get_coordinates property

super_area property

add(person, activity='leisure')

Parameters:

Name Type Description Default
person
required
activity

(Default value = "leisure")

'leisure'
Source code in june/groups/leisure/social_venue.py
38
39
40
41
42
43
44
45
46
47
def add(self, person, activity="leisure"):
    """

    Args:
        person: 
        activity: (Default value = "leisure")

    """
    self.subgroups[0].append(person)
    setattr(person.subgroups, activity, self.subgroups[0])

SocialVenueError

Bases: BaseException

Source code in june/groups/leisure/social_venue.py
22
23
24
class SocialVenueError(BaseException):
    """ """
    pass

SocialVenues

Bases: Supergroup

Source code in june/groups/leisure/social_venue.py
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
class SocialVenues(Supergroup):
    """ """
    venue_class = SocialVenue

    def __init__(self, social_venues: List[venue_class], make_tree=True):
        super().__init__(members=social_venues)
        # Use a safer logging approach that works in both MPI and non-MPI modes
        current_rank = 0 if not mpi_available else mpi_rank
        logger.info(f"Domain {current_rank} has {len(self)} {self.spec}(s)")
        self.ball_tree = None
        if make_tree:
            if not social_venues:
                logger.warning(f"No social venues of type {self.spec} in this domain")
            else:
                self.make_tree()

    @classmethod
    def from_coordinates(
        cls,
        coordinates: List[np.array],
        super_areas: Optional[SuperAreas],
        max_distance_to_area=10,
        **kwargs,
    ):
        """

        Args:
            coordinates (List[np.array]): 
            super_areas (Optional[SuperAreas]): 
            max_distance_to_area: (Default value = 10)
            **kwargs: 

        """
        if len(coordinates) == 0:
            return cls([], **kwargs)

        if super_areas:
            super_areas, distances = super_areas.get_closest_super_areas(
                coordinates, k=1, return_distance=True
            )
            distances_close = np.where(distances < max_distance_to_area)
            coordinates = coordinates[distances_close]
            super_areas = [super_areas[i] for i in distances_close[0]]

        social_venues = []

        for i, coord in enumerate(coordinates):
            sv = cls.venue_class()  # Pass disease_config here
            if super_areas:
                super_area = super_areas[i]
            else:
                super_area = None
            sv.coordinates = coord
            if super_areas:
                area, dist = Areas(super_area.areas).get_closest_area(
                    coordinates=coord, return_distance=True
                )

                if dist > max_distance_to_area:
                    continue

                sv.area = area
            social_venues.append(sv)

        return cls(social_venues, **kwargs)

    @classmethod
    def for_super_areas(
        cls, super_areas: List[SuperArea], coordinates_filename: str = None
    ):
        """

        Args:
            super_areas (List[SuperArea]): 
            coordinates_filename (str, optional): (Default value = None)

        """
        if coordinates_filename is None:
            coordinates_filename = cls.default_coordinates_filename
        sv_coordinates = pd.read_csv(coordinates_filename, index_col=0).values

        # Generate social venues using the coordinates
        social_venues_instance = cls.from_coordinates(
            sv_coordinates, super_areas=super_areas
        )

        # Prepare sample data for visualization
        venue_data = [
            {
                "Venue ID": venue.id,
                "Super Area": venue.area.super_area.name if venue.area and venue.area.super_area else "Unknown",
                "Coordinates": venue.coordinates,
                "Assigned Area": venue.area.name if venue.area else "None",
            }
            for venue in social_venues_instance.members
        ]

        # Convert sample data to DataFrame for better readability
        df_venues = pd.DataFrame(venue_data)
        print("\n===== Gaps in ID represent the venues that were created that did not fulfill the criteria required =====")
        print("\n===== Sample of Social Venues Created from Coordinates =====")
        print(df_venues)

        return social_venues_instance


    @classmethod
    def for_areas(cls, areas: Areas, coordinates_filename: str = None):
        """

        Args:
            areas (Areas): 
            coordinates_filename (str, optional): (Default value = None)

        """
        if coordinates_filename is None:
            coordinates_filename = cls.default_coordinates_filename
        super_areas = SuperAreas([area.super_area for area in areas])
        return cls.for_super_areas(super_areas, coordinates_filename)

    @classmethod
    def for_geography(cls, geography: Geography, coordinates_filename: str = None):
        """

        Args:
            geography (Geography): 
            coordinates_filename (str, optional): (Default value = None)

        """
        if coordinates_filename is None:
            coordinates_filename = cls.default_coordinates_filename

        return cls.for_super_areas(geography.super_areas, coordinates_filename)

    @classmethod
    def distribute_for_areas(
        cls,
        areas: List[Area],
        venues_per_capita: float = None,
        venues_per_area: int = None,
    ):
        """Generates social venues in the given areas.

        Args:
            areas (List[Area]): list of areas to generate the venues in
            venues_per_capita (float, optional): number of venues per person in each area. (Default value = None)
            venues_per_area (int, optional): number of venues in each area. (Default value = None)

        """
        if venues_per_area is not None and venues_per_capita is not None:
            raise SocialVenueError(
                "Please specify only one of venues_per_capita or venues_per_area."
            )
        social_venues = []
        if venues_per_area is not None:
            for area in areas:
                for _ in range(venues_per_area):
                    sv = cls.venue_class()
                    sv.area = area
                    social_venues.append(sv)
        elif venues_per_capita is not None:
            for area in areas:
                area_population = len(area.people)
                for _ in range(int(np.ceil(venues_per_capita * area_population))):
                    sv = cls.venue_class()
                    sv.area = area
                    sv.coordinates = area.coordinates
                    social_venues.append(sv)
        else:
            raise SocialVenueError(
                "Specify one of venues_per_capita or venues_per_area"
            )
        return cls(social_venues)

    @classmethod
    def distribute_for_super_areas(
        cls, super_areas: List[SuperArea], venues_per_super_area=1, venues_per_capita=1
    ):
        """Generates social venues in the given super areas.

        Args:
            super_areas (List[SuperArea]): list of areas to generate the venues in
            venues_per_super_area: how many venus per super_area to generate (Default value = 1)
            venues_per_capita: (Default value = 1)

        """
        if venues_per_super_area is not None and venues_per_capita is not None:
            raise SocialVenueError(
                "Please specify only one of venues_per_capita or venues_per_area."
            )
        social_venues = []
        if venues_per_super_area is not None:
            for area in super_areas:
                for _ in range(venues_per_super_area):
                    sv = cls.venue_class()
                    sv.area = area
                    social_venues.append(sv)
        elif venues_per_capita is not None:
            for super_area in super_areas:
                super_area_population = len(super_area.people)
                for _ in range(int(np.ceil(venues_per_capita * super_area_population))):
                    sv = cls.venue_class()
                    area = Areas(super_area.areas).get_closest_area(
                        coordinates=super_area.coordinates
                    )
                    sv.area = area
                    sv.coordinates = area.coordinates
                    social_venues.append(sv)
        else:
            raise SocialVenueError(
                "Specify one of venues_per_capita or venues_per_area"
            )
        return cls(social_venues)

    def make_tree(self):
        """ """
        self.ball_tree = BallTree(
            np.array([np.deg2rad(sv.coordinates) for sv in self]), metric="haversine"
        )

    def add_to_areas(self, areas: Areas):
        """Adds all venues to the closest super area

        Args:
            areas (Areas): 

        """
        for venue in self:
            if not hasattr(venue, "coordinates"):
                raise SocialVenueError(
                    "Can't add to super area if venues don't have coordiantes."
                )
            venue.area = areas.get_closest_areas(venue.coordinates)[0]

    def get_closest_venues(self, coordinates, k=1):
        """Queries the ball tree for the closests venues.

        Args:
            coordinates: coordinates in the format [Latitude, Longitude]
            k: number of neighbours desired (Default value = 1)

        """
        if not self.members:
            return
        if self.ball_tree is None:
            raise SocialVenueError("Initialise ball tree first with self.make_tree()")
        venue_idxs = self.ball_tree.query(
            np.deg2rad(coordinates).reshape(1, -1), return_distance=False, k=k
        ).flatten()
        social_venues = self.members
        return [social_venues[idx] for idx in venue_idxs]

    def get_venues_in_radius(self, coordinates, radius=5):
        """Queries the ball tree for the closests venues.

        Args:
            coordinates: coordinates in the format [Latitude, Longitude]
            radius: radius in km to query (Default value = 5)

        """
        if not self.members:
            return
        if self.ball_tree is None:
            raise SocialVenueError("Initialise ball tree first with self.make_tree()")
        radius = radius / earth_radius
        venue_idxs, _ = self.ball_tree.query_radius(
            np.deg2rad(coordinates).reshape(1, -1),
            r=radius,
            sort_results=True,
            return_distance=True,
        )
        venue_idxs = venue_idxs[0]
        if not venue_idxs.size:
            return None
        social_venues = self.members
        return [social_venues[idx] for idx in venue_idxs]

    def get_leisure_subgroup(self, person, subgroup_type, to_send_abroad):
        """

        Args:
            person: 
            subgroup_type: 
            to_send_abroad: 

        """
        return self[subgroup_type]

add_to_areas(areas)

Adds all venues to the closest super area

Parameters:

Name Type Description Default
areas Areas
required
Source code in june/groups/leisure/social_venue.py
286
287
288
289
290
291
292
293
294
295
296
297
298
def add_to_areas(self, areas: Areas):
    """Adds all venues to the closest super area

    Args:
        areas (Areas): 

    """
    for venue in self:
        if not hasattr(venue, "coordinates"):
            raise SocialVenueError(
                "Can't add to super area if venues don't have coordiantes."
            )
        venue.area = areas.get_closest_areas(venue.coordinates)[0]

distribute_for_areas(areas, venues_per_capita=None, venues_per_area=None) classmethod

Generates social venues in the given areas.

Parameters:

Name Type Description Default
areas List[Area]

list of areas to generate the venues in

required
venues_per_capita float

number of venues per person in each area. (Default value = None)

None
venues_per_area int

number of venues in each area. (Default value = None)

None
Source code in june/groups/leisure/social_venue.py
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
@classmethod
def distribute_for_areas(
    cls,
    areas: List[Area],
    venues_per_capita: float = None,
    venues_per_area: int = None,
):
    """Generates social venues in the given areas.

    Args:
        areas (List[Area]): list of areas to generate the venues in
        venues_per_capita (float, optional): number of venues per person in each area. (Default value = None)
        venues_per_area (int, optional): number of venues in each area. (Default value = None)

    """
    if venues_per_area is not None and venues_per_capita is not None:
        raise SocialVenueError(
            "Please specify only one of venues_per_capita or venues_per_area."
        )
    social_venues = []
    if venues_per_area is not None:
        for area in areas:
            for _ in range(venues_per_area):
                sv = cls.venue_class()
                sv.area = area
                social_venues.append(sv)
    elif venues_per_capita is not None:
        for area in areas:
            area_population = len(area.people)
            for _ in range(int(np.ceil(venues_per_capita * area_population))):
                sv = cls.venue_class()
                sv.area = area
                sv.coordinates = area.coordinates
                social_venues.append(sv)
    else:
        raise SocialVenueError(
            "Specify one of venues_per_capita or venues_per_area"
        )
    return cls(social_venues)

distribute_for_super_areas(super_areas, venues_per_super_area=1, venues_per_capita=1) classmethod

Generates social venues in the given super areas.

Parameters:

Name Type Description Default
super_areas List[SuperArea]

list of areas to generate the venues in

required
venues_per_super_area

how many venus per super_area to generate (Default value = 1)

1
venues_per_capita

(Default value = 1)

1
Source code in june/groups/leisure/social_venue.py
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
@classmethod
def distribute_for_super_areas(
    cls, super_areas: List[SuperArea], venues_per_super_area=1, venues_per_capita=1
):
    """Generates social venues in the given super areas.

    Args:
        super_areas (List[SuperArea]): list of areas to generate the venues in
        venues_per_super_area: how many venus per super_area to generate (Default value = 1)
        venues_per_capita: (Default value = 1)

    """
    if venues_per_super_area is not None and venues_per_capita is not None:
        raise SocialVenueError(
            "Please specify only one of venues_per_capita or venues_per_area."
        )
    social_venues = []
    if venues_per_super_area is not None:
        for area in super_areas:
            for _ in range(venues_per_super_area):
                sv = cls.venue_class()
                sv.area = area
                social_venues.append(sv)
    elif venues_per_capita is not None:
        for super_area in super_areas:
            super_area_population = len(super_area.people)
            for _ in range(int(np.ceil(venues_per_capita * super_area_population))):
                sv = cls.venue_class()
                area = Areas(super_area.areas).get_closest_area(
                    coordinates=super_area.coordinates
                )
                sv.area = area
                sv.coordinates = area.coordinates
                social_venues.append(sv)
    else:
        raise SocialVenueError(
            "Specify one of venues_per_capita or venues_per_area"
        )
    return cls(social_venues)

for_areas(areas, coordinates_filename=None) classmethod

Parameters:

Name Type Description Default
areas Areas
required
coordinates_filename str

(Default value = None)

None
Source code in june/groups/leisure/social_venue.py
172
173
174
175
176
177
178
179
180
181
182
183
184
@classmethod
def for_areas(cls, areas: Areas, coordinates_filename: str = None):
    """

    Args:
        areas (Areas): 
        coordinates_filename (str, optional): (Default value = None)

    """
    if coordinates_filename is None:
        coordinates_filename = cls.default_coordinates_filename
    super_areas = SuperAreas([area.super_area for area in areas])
    return cls.for_super_areas(super_areas, coordinates_filename)

for_geography(geography, coordinates_filename=None) classmethod

Parameters:

Name Type Description Default
geography Geography
required
coordinates_filename str

(Default value = None)

None
Source code in june/groups/leisure/social_venue.py
186
187
188
189
190
191
192
193
194
195
196
197
198
@classmethod
def for_geography(cls, geography: Geography, coordinates_filename: str = None):
    """

    Args:
        geography (Geography): 
        coordinates_filename (str, optional): (Default value = None)

    """
    if coordinates_filename is None:
        coordinates_filename = cls.default_coordinates_filename

    return cls.for_super_areas(geography.super_areas, coordinates_filename)

for_super_areas(super_areas, coordinates_filename=None) classmethod

Parameters:

Name Type Description Default
super_areas List[SuperArea]
required
coordinates_filename str

(Default value = None)

None
Source code in june/groups/leisure/social_venue.py
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
@classmethod
def for_super_areas(
    cls, super_areas: List[SuperArea], coordinates_filename: str = None
):
    """

    Args:
        super_areas (List[SuperArea]): 
        coordinates_filename (str, optional): (Default value = None)

    """
    if coordinates_filename is None:
        coordinates_filename = cls.default_coordinates_filename
    sv_coordinates = pd.read_csv(coordinates_filename, index_col=0).values

    # Generate social venues using the coordinates
    social_venues_instance = cls.from_coordinates(
        sv_coordinates, super_areas=super_areas
    )

    # Prepare sample data for visualization
    venue_data = [
        {
            "Venue ID": venue.id,
            "Super Area": venue.area.super_area.name if venue.area and venue.area.super_area else "Unknown",
            "Coordinates": venue.coordinates,
            "Assigned Area": venue.area.name if venue.area else "None",
        }
        for venue in social_venues_instance.members
    ]

    # Convert sample data to DataFrame for better readability
    df_venues = pd.DataFrame(venue_data)
    print("\n===== Gaps in ID represent the venues that were created that did not fulfill the criteria required =====")
    print("\n===== Sample of Social Venues Created from Coordinates =====")
    print(df_venues)

    return social_venues_instance

from_coordinates(coordinates, super_areas, max_distance_to_area=10, **kwargs) classmethod

Parameters:

Name Type Description Default
coordinates List[array]
required
super_areas Optional[SuperAreas]
required
max_distance_to_area

(Default value = 10)

10
**kwargs
{}
Source code in june/groups/leisure/social_venue.py
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
@classmethod
def from_coordinates(
    cls,
    coordinates: List[np.array],
    super_areas: Optional[SuperAreas],
    max_distance_to_area=10,
    **kwargs,
):
    """

    Args:
        coordinates (List[np.array]): 
        super_areas (Optional[SuperAreas]): 
        max_distance_to_area: (Default value = 10)
        **kwargs: 

    """
    if len(coordinates) == 0:
        return cls([], **kwargs)

    if super_areas:
        super_areas, distances = super_areas.get_closest_super_areas(
            coordinates, k=1, return_distance=True
        )
        distances_close = np.where(distances < max_distance_to_area)
        coordinates = coordinates[distances_close]
        super_areas = [super_areas[i] for i in distances_close[0]]

    social_venues = []

    for i, coord in enumerate(coordinates):
        sv = cls.venue_class()  # Pass disease_config here
        if super_areas:
            super_area = super_areas[i]
        else:
            super_area = None
        sv.coordinates = coord
        if super_areas:
            area, dist = Areas(super_area.areas).get_closest_area(
                coordinates=coord, return_distance=True
            )

            if dist > max_distance_to_area:
                continue

            sv.area = area
        social_venues.append(sv)

    return cls(social_venues, **kwargs)

get_closest_venues(coordinates, k=1)

Queries the ball tree for the closests venues.

Parameters:

Name Type Description Default
coordinates

coordinates in the format [Latitude, Longitude]

required
k

number of neighbours desired (Default value = 1)

1
Source code in june/groups/leisure/social_venue.py
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
def get_closest_venues(self, coordinates, k=1):
    """Queries the ball tree for the closests venues.

    Args:
        coordinates: coordinates in the format [Latitude, Longitude]
        k: number of neighbours desired (Default value = 1)

    """
    if not self.members:
        return
    if self.ball_tree is None:
        raise SocialVenueError("Initialise ball tree first with self.make_tree()")
    venue_idxs = self.ball_tree.query(
        np.deg2rad(coordinates).reshape(1, -1), return_distance=False, k=k
    ).flatten()
    social_venues = self.members
    return [social_venues[idx] for idx in venue_idxs]

get_leisure_subgroup(person, subgroup_type, to_send_abroad)

Parameters:

Name Type Description Default
person
required
subgroup_type
required
to_send_abroad
required
Source code in june/groups/leisure/social_venue.py
343
344
345
346
347
348
349
350
351
352
def get_leisure_subgroup(self, person, subgroup_type, to_send_abroad):
    """

    Args:
        person: 
        subgroup_type: 
        to_send_abroad: 

    """
    return self[subgroup_type]

get_venues_in_radius(coordinates, radius=5)

Queries the ball tree for the closests venues.

Parameters:

Name Type Description Default
coordinates

coordinates in the format [Latitude, Longitude]

required
radius

radius in km to query (Default value = 5)

5
Source code in june/groups/leisure/social_venue.py
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
def get_venues_in_radius(self, coordinates, radius=5):
    """Queries the ball tree for the closests venues.

    Args:
        coordinates: coordinates in the format [Latitude, Longitude]
        radius: radius in km to query (Default value = 5)

    """
    if not self.members:
        return
    if self.ball_tree is None:
        raise SocialVenueError("Initialise ball tree first with self.make_tree()")
    radius = radius / earth_radius
    venue_idxs, _ = self.ball_tree.query_radius(
        np.deg2rad(coordinates).reshape(1, -1),
        r=radius,
        sort_results=True,
        return_distance=True,
    )
    venue_idxs = venue_idxs[0]
    if not venue_idxs.size:
        return None
    social_venues = self.members
    return [social_venues[idx] for idx in venue_idxs]

make_tree()

Source code in june/groups/leisure/social_venue.py
280
281
282
283
284
def make_tree(self):
    """ """
    self.ball_tree = BallTree(
        np.array([np.deg2rad(sv.coordinates) for sv in self]), metric="haversine"
    )