particle.py 21 KB
Newer Older
1
2
3
"""The world module provides the interface of the simulation world. In the simulation world
all the data of the particles, tiles, and locations are stored.
It also have the the coordination system and stated the maximum of the x and y coordinate.
Ahmad Reza's avatar
Ahmad Reza committed
4

5
 .. todo:: What happens if the maximum y or x axis is passed? Either the start from the other side or turns back.
Ahmad Reza's avatar
Ahmad Reza committed
6
"""
7
import importlib
Karol Actun's avatar
Karol Actun committed
8
import logging
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
61
62
63
64
65
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
import random
import threading
import os
import datetime

from lib import csv_generator, particle, tile, location, vis3d
from lib.swarm_sim_header import eprint


class World:
    def __init__(self, config_data):
        """
        Initializing the world constructor
        :param config_data: configuration data from config.ini file
        """
        self.__round_counter = 1
        self.__end = False

        self.init_particles = []
        self.particle_id_counter = 0
        self.particles = []
        self.particle_map_coordinates = {}
        self.particle_map_id = {}
        self.particles_created = []
        self.particle_rm = []
        self.__particle_deleted = False
        self.new_particle = None

        self.tiles = []
        self.tile_map_coordinates = {}
        self.tile_map_id = {}
        self.tiles_created = []
        self.tiles_rm = []

        self.__tile_deleted = False
        self.new_tile = None
        self.__tile_deleted = False

        self.locations = []
        self.location_map_coordinates = {}
        self.location_map_id = {}
        self.locations_created = []
        self.locations_rm = []
        self.__location_deleted = False
        self.new_location = None

        self.config_data = config_data
        self.grid = config_data.grid

        self.csv_round = csv_generator.CsvRoundData(scenario=config_data.scenario,
                                                    solution=config_data.solution,
                                                    seed=config_data.seed_value,
                                                    directory=config_data.direction_name)

        if config_data.visualization:
            self.vis = vis3d.Visualization(self)
        else:
            self.vis = None

        mod = importlib.import_module('scenario.' + self.config_data.scenario)

        if config_data.visualization:
            import threading
            x = threading.Thread(target=mod.scenario, args=(self,))
            self.vis.wait_for_thread(x, "loading scenario... please wait.", "Loading Scenario")
        else:
            mod.scenario(self)

        if self.config_data.particle_random_order:
            random.shuffle(self.particles)

    def reset(self):
        """
        resets everything (particles, tiles, locations) except for the logging in system.log and in the csv file...
        reloads the scenario.
        :return:
        """
        self.__round_counter = 1
        self.__end = False

        self.init_particles = []
        self.particle_id_counter = 0
        self.particles = []
        self.particles_created = []
        self.particle_rm = []
        self.particle_map_coordinates = {}
        self.particle_map_id = {}
        self.__particle_deleted = False
        self.new_particle = None
Ahmad Reza's avatar
Ahmad Reza committed
98

99
100
101
102
103
104
105
        self.tiles = []
        self.tiles_created = []
        self.tiles_rm = []
        self.tile_map_coordinates = {}
        self.tile_map_id = {}
        self.__tile_deleted = False
        self.new_tile = None
Ahmad Reza's avatar
Ahmad Reza committed
106

107
108
109
110
111
112
113
        self.locations = []
        self.locations_created = []
        self.location_map_coordinates = {}
        self.location_map_id = {}
        self.locations_rm = []
        self.__location_deleted = False
        self.new_location = None
Ahmad Reza's avatar
Ahmad Reza committed
114

115
116
        if self.config_data.visualization:
            self.vis.reset()
Ahmad Reza's avatar
Ahmad Reza committed
117

118
        mod = importlib.import_module('scenario.' + self.config_data.scenario)
Ahmad Reza's avatar
Ahmad Reza committed
119

120
121
122
123
124
        if self.config_data.visualization:
            # if visualization is on, run the scenario in a separate thread and show that the program runs..
            x = threading.Thread(target=mod.scenario, args=(self,))
            self.vis.wait_for_thread(x, "loading scenario... please wait.", "Loading Scenario")
            self.vis.update_visualization_data()
Ahmad Reza's avatar
Ahmad Reza committed
125
126

        else:
127
128
            # if no vis, just run the scenario on the main thread
            mod.scenario(self)
Ahmad Reza's avatar
Ahmad Reza committed
129

130
131
        if self.config_data.particle_random_order:
            random.shuffle(self.particles)
Ahmad Reza's avatar
Ahmad Reza committed
132

133
    def save_scenario(self):
Ahmad Reza's avatar
Ahmad Reza committed
134

135
136
137
        # create scenario folder, if it doesn't already exist.
        if not os.path.exists("scenario") or not os.path.isdir("scenario"):
            os.mkdir("scenario")
Ahmad Reza's avatar
Ahmad Reza committed
138

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
        # if the scenario folder exists, try to create and save the new scenario file, if it fails print the error.
        if os.path.exists("scenario") and os.path.isdir("scenario"):
            now = datetime.datetime.now()
            filename = str("scenario/%d-%d-%d_%d-%d-%d_scenario.py"
                           % (now.year, now.month, now.day, now.hour, now.minute, now.second))
            try:
                f = open(filename, "w+")
                f.write("def scenario(world):\n")
                for p in self.particle_map_coordinates.values():
                    f.write("\tworld.add_particle(%s, color=%s)\n" % (str(p.coordinates), str(p.get_color())))
                for t in self.tile_map_coordinates.values():
                    f.write("\tworld.add_tile(%s, color=%s)\n" % (str(t.coordinates), str(t.get_color())))
                for l in self.location_map_coordinates.values():
                    f.write("\tworld.add_location(%s, color=%s)\n" % (str(l.coordinates), str(l.get_color())))
                f.flush()
                f.close()
            except IOError as e:
                eprint(e)
Karol Actun's avatar
Karol Actun committed
157

158
159
160
            # checks if the file exists. If not, some unknown error occured while saving.
            if not os.path.exists(filename) or not os.path.isfile(filename):
                eprint("Error: scenario couldn't be saved due to unknown reasons.")
Ahmad Reza Cheraghi's avatar
Ahmad Reza Cheraghi committed
161
        else:
162
            eprint("\"scenario\" folder couldn't be created.")
Ahmad Reza's avatar
Ahmad Reza committed
163

164
165
166
167
168
169
    def csv_aggregator(self):
        self.csv_round.aggregate_metrics()
        particle_csv = csv_generator.CsvParticleFile(self.config_data.direction_name)
        for p in self.particles:
            particle_csv.write_particle(p)
        particle_csv.csv_file.close()
Ahmad Reza's avatar
Ahmad Reza committed
170

171
172
173
    def set_successful_end(self):
        self.csv_round.success()
        # self.set_end()
Ahmad Reza's avatar
Ahmad Reza committed
174

175
    def get_max_round(self):
Ahmad Reza Cheraghi's avatar
Ahmad Reza Cheraghi committed
176
        """
177
178
179
        The max round number
    
        :return: maximum round number
Ahmad Reza's avatar
Ahmad Reza committed
180
        """
181
        return self.config_data.max_round
Ahmad Reza's avatar
Ahmad Reza committed
182

183
    def get_actual_round(self):
Ahmad Reza's avatar
Ahmad Reza committed
184
        """
185
        The actual round number
Ahmad Reza's avatar
Ahmad Reza committed
186

187
        :return: actual round number
Ahmad Reza's avatar
Ahmad Reza committed
188
        """
189
        return self.__round_counter
Ahmad Reza's avatar
Ahmad Reza committed
190

191
    def set_unsuccessful_end(self):
Ahmad Reza's avatar
Ahmad Reza committed
192
        """
193
        Allows to terminate before the max round is reached
Ahmad Reza's avatar
Ahmad Reza committed
194
        """
195
        self.__end = True
Ahmad Reza's avatar
Ahmad Reza committed
196

197
    def get_end(self):
Ahmad Reza's avatar
Ahmad Reza committed
198
        """
199
            Returns the end parameter values either True or False
Ahmad Reza's avatar
Ahmad Reza committed
200
        """
201
        return self.__end
Ahmad Reza's avatar
Ahmad Reza committed
202

203
    def inc_round_counter_by(self, number=1):
Ahmad Reza's avatar
Ahmad Reza committed
204
        """
205
        Increases the the round counter by
Ahmad Reza's avatar
Ahmad Reza committed
206

207
        :return:
Ahmad Reza's avatar
Ahmad Reza committed
208
        """
209
        self.__round_counter += number
Ahmad Reza's avatar
Ahmad Reza committed
210

211
    def get_solution(self):
Ahmad Reza's avatar
Ahmad Reza committed
212
        """
213
        actual solution name
Ahmad Reza's avatar
Ahmad Reza committed
214

215
        :return: actual solution name
Ahmad Reza's avatar
Ahmad Reza committed
216
        """
217
        return self.config_data.solution
Ahmad Reza's avatar
Ahmad Reza committed
218

219
    def get_amount_of_particles(self):
Ahmad Reza's avatar
Ahmad Reza committed
220
        """
221
        Returns the actual number of particles in the world
Ahmad Reza's avatar
Ahmad Reza committed
222

223
        :return: The actual number of Particles
Ahmad Reza's avatar
Ahmad Reza committed
224
        """
225
        return len(self.particles)
Ahmad Reza's avatar
Ahmad Reza committed
226

227
    def get_particle_list(self):
Ahmad Reza's avatar
Ahmad Reza committed
228
        """
229
        Returns the actual number of particles in the world
Ahmad Reza's avatar
Ahmad Reza committed
230

231
        :return: The actual number of Particles
Ahmad Reza's avatar
Ahmad Reza committed
232
        """
233
        return self.particles
Ahmad Reza's avatar
Ahmad Reza committed
234

235
    def get_particle_map_coordinates(self):
Ahmad Reza's avatar
Ahmad Reza committed
236
        """
237
        Get a dictionary with all particles mapped with their actual coordinates
Ahmad Reza's avatar
Ahmad Reza committed
238

239
        :return: a dictionary with particles and their coordinates
Ahmad Reza's avatar
Ahmad Reza committed
240
        """
241
        return self.particle_map_coordinates
Ahmad Reza's avatar
Ahmad Reza committed
242

243
    def get_particle_map_id(self):
Ahmad Reza's avatar
Ahmad Reza committed
244
        """
245
        Get a dictionary with all particles mapped with their own ids
Ahmad Reza's avatar
Ahmad Reza committed
246

247
        :return: a dictionary with particles and their own ids
Ahmad Reza's avatar
Ahmad Reza committed
248
        """
249
        return self.particle_map_id
Ahmad Reza's avatar
Ahmad Reza committed
250

251
    def get_amount_of_tiles(self):
Ahmad Reza's avatar
Ahmad Reza committed
252
        """
253
        Returns the actual number of particles in the world
Ahmad Reza's avatar
Ahmad Reza committed
254

255
        :return: The actual number of Particles
Ahmad Reza's avatar
Ahmad Reza committed
256
        """
257
        return len(self.tiles)
Ahmad Reza's avatar
Ahmad Reza committed
258

259
    def get_tiles_list(self):
Ahmad Reza's avatar
Ahmad Reza committed
260
        """
261
        Returns the actual number of tiles in the world
Ahmad Reza's avatar
Ahmad Reza committed
262

263
        :return: a list of all the tiles in the world
Ahmad Reza's avatar
Ahmad Reza committed
264
        """
265
        return self.tiles
Ahmad Reza's avatar
Ahmad Reza committed
266

267
    def get_tile_map_coordinates(self):
Ahmad Reza's avatar
Ahmad Reza committed
268
        """
269
        Get a dictionary with all tiles mapped with their actual coordinates
Ahmad Reza's avatar
Ahmad Reza committed
270

271
        :return: a dictionary with particles and their coordinates
Ahmad Reza's avatar
Ahmad Reza committed
272
        """
273
        return self.tile_map_coordinates
Ahmad Reza's avatar
Ahmad Reza committed
274

275
    def get_tile_map_id(self):
Ahmad Reza's avatar
Ahmad Reza committed
276
        """
277
        Get a dictionary with all particles mapped with their own ids
Ahmad Reza's avatar
Ahmad Reza committed
278

279
        :return: a dictionary with particles and their own ids
Ahmad Reza's avatar
Ahmad Reza committed
280
        """
281
        return self.tile_map_id
Ahmad Reza's avatar
Ahmad Reza committed
282

283
    def get_amount_of_locations(self):
Ahmad Reza's avatar
Ahmad Reza committed
284
        """
285
        Returns the actual number of locations in the world
Ahmad Reza's avatar
Ahmad Reza committed
286

287
        :return: The actual number of locations
Ahmad Reza's avatar
Ahmad Reza committed
288
        """
289
        return len(self.locations)
Ahmad Reza's avatar
Ahmad Reza committed
290

291
    def get_location_list(self):
Ahmad Reza's avatar
Ahmad Reza committed
292
        """
293
        Returns the actual number of locations in the world
Ahmad Reza's avatar
Ahmad Reza committed
294

295
        :return: The actual number of locations
Ahmad Reza's avatar
Ahmad Reza committed
296
        """
297
        return self.locations
Ahmad Reza's avatar
Ahmad Reza committed
298

299
    def get_location_map_coordinates(self):
Ahmad Reza's avatar
Ahmad Reza committed
300
        """
301
        Get a dictionary with all locations mapped with their actual coordinates
Ahmad Reza's avatar
Ahmad Reza committed
302

303
        :return: a dictionary with locations and their coordinates
Ahmad Reza's avatar
Ahmad Reza committed
304
        """
305
        return self.location_map_coordinates
Ahmad Reza's avatar
Ahmad Reza committed
306

307
    def get_location_map_id(self):
Ahmad Reza's avatar
Ahmad Reza committed
308
        """
309
        Get a dictionary with all locations mapped with their own ids
Ahmad Reza's avatar
Ahmad Reza committed
310

311
        :return: a dictionary with locations and their own ids
Ahmad Reza's avatar
Ahmad Reza committed
312
        """
313
        return self.location_map_id
Ahmad Reza's avatar
Ahmad Reza committed
314

315
    def get_x_size(self):
Ahmad Reza's avatar
Ahmad Reza committed
316
317
        """

318
        :return: Returns the maximal x size of the world
Ahmad Reza's avatar
Ahmad Reza committed
319
        """
320
        return self.config_data.size_x
Ahmad Reza's avatar
Ahmad Reza committed
321

322
    def get_y_size(self):
Ahmad Reza's avatar
Ahmad Reza committed
323
        """
324
        :return: Returns the maximal y size of the world
Ahmad Reza's avatar
Ahmad Reza committed
325
        """
326
        return self.config_data.size_y
Ahmad Reza's avatar
Ahmad Reza committed
327

328
    def get_z_size(self):
Ahmad Reza's avatar
Ahmad Reza committed
329
330
        """

331
        :return: Returns the maximal z size of the world
Ahmad Reza's avatar
Ahmad Reza committed
332
        """
333
        return self.config_data.size_z
Ahmad Reza's avatar
Ahmad Reza committed
334

335
    def get_size(self):
Ahmad Reza's avatar
Ahmad Reza committed
336
        """
337
        :return: Returns the maximal (x,y) size of the world as a tupel
Ahmad Reza's avatar
Ahmad Reza committed
338
        """
339
        return self.config_data.size_x, self.config_data.size_y
Ahmad Reza's avatar
Ahmad Reza committed
340

341
342
    def get_tile_deleted(self):
        return self.__tile_deleted
Ahmad Reza's avatar
Ahmad Reza committed
343

344
345
    def get_particle_deleted(self):
        return self.__particle_deleted
Ahmad Reza's avatar
Ahmad Reza committed
346

347
348
    def get_location_deleted(self):
        return self.__location_deleted
Ahmad Reza's avatar
Ahmad Reza committed
349

350
351
    def set_tile_deleted(self):
        self.__tile_deleted = False
Ahmad Reza's avatar
Ahmad Reza committed
352

353
354
    def set_particle_deleted(self):
        self.__particle_deleted = False
Ahmad Reza's avatar
Ahmad Reza committed
355

356
357
    def set_location_deleted(self):
        self.__location_deleted = False
Ahmad Reza's avatar
Ahmad Reza committed
358

359
    def add_particle(self, coordinates, color=None, new_class=particle.Particle):
Ahmad Reza's avatar
Ahmad Reza committed
360
        """
361
        Add a particle to the world database
Ahmad Reza's avatar
Ahmad Reza committed
362

363
364
365
        :param coordinates: The x coordinate of the particle
        :param color: The color of the particle
        :return: Added Matter; False: Unsuccessful
Ahmad Reza's avatar
Ahmad Reza committed
366
        """
367
368
369
        if isinstance(coordinates, int) or isinstance(coordinates, float):
            coordinates = (coordinates, color, 0.0)
            color = None
Ahmad Reza's avatar
Ahmad Reza committed
370

371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
        elif len(coordinates) == 2:
            coordinates = (coordinates[0], coordinates[1], 0.0)

        if len(self.particles) < self.config_data.max_particles:
            if self.grid.are_valid_coordinates(coordinates):
                if coordinates not in self.get_particle_map_coordinates():
                    if color is None:
                        color = self.config_data.particle_color
                    self.particle_id_counter += 1
                    self.new_particle = new_class(self, coordinates, color, self.particle_id_counter)
                    if self.vis is not None:
                        self.vis.particle_changed(self.new_particle)
                    self.particles_created.append(self.new_particle)
                    self.particle_map_coordinates[self.new_particle.coordinates] = self.new_particle
                    self.particle_map_id[self.new_particle.get_id()] = self.new_particle
                    self.particles.append(self.new_particle)
                    self.csv_round.update_particle_num(len(self.particles))
                    self.init_particles.append(self.new_particle)
                    self.new_particle.created = True
                    logging.info("Created particle at %s", self.new_particle.coordinates)
                    return self.new_particle
Ahmad Reza's avatar
Ahmad Reza committed
392
393

                else:
394
                    logging.info("there is already a particle on %s" % str(coordinates))
Ahmad Reza's avatar
Ahmad Reza committed
395
396
                    return False
            else:
397
                logging.info("%s is not a valid location!" % str(coordinates))
Ahmad Reza's avatar
Ahmad Reza committed
398
399
                return False
        else:
400
            logging.info("Max of particles reached and no more particles can be created")
Ahmad Reza's avatar
Ahmad Reza committed
401
402
            return False

403
404
    def remove_particle(self, particle_id):
        """ Removes a particle with a given particle id from the world database
405

Ahmad Reza's avatar
Ahmad Reza committed
406

407
408
        :param particle_id: particle id
        :return: True: Successful removed; False: Unsuccessful
Ahmad Reza's avatar
Ahmad Reza committed
409
        """
410
411
412
413
414
415
416
417
418
419
420
        rm_particle = self.particle_map_id[particle_id]
        if rm_particle:
            self.particles.remove(rm_particle)
            del self.particle_map_coordinates[rm_particle.coordinates]
            del self.particle_map_id[particle_id]
            self.particle_rm.append(rm_particle)
            if self.vis is not None:
                self.vis.remove_particle(rm_particle)
            self.csv_round.update_particle_num(len(self.particles))
            self.csv_round.update_metrics(particle_deleted=1)
            self.__particle_deleted = True
421
            return True
Ahmad Reza's avatar
Ahmad Reza committed
422
        else:
423
424
            return False

425
    def remove_particle_on(self, coordinates):
Ahmad Reza's avatar
Ahmad Reza committed
426
        """
427
        Removes a particle on a give coordinate from to the world database
Ahmad Reza's avatar
Ahmad Reza committed
428

429
430
        :param coordinates: A tuple that includes the x and y coordinates
        :return: True: Successful removed; False: Unsuccessful
Ahmad Reza's avatar
Ahmad Reza committed
431
        """
432
433
        if coordinates in self.particle_map_coordinates:
            return self.remove_particle(self.particle_map_coordinates[coordinates].get_id())
Ahmad Reza's avatar
Ahmad Reza committed
434
        else:
Karol Actun's avatar
Karol Actun committed
435
            return False
Ahmad Reza's avatar
Ahmad Reza committed
436

437
    def add_tile(self, coordinates, color=None):
Ahmad Reza's avatar
Ahmad Reza committed
438
        """
439
440
441
442
        Adds a tile to the world database
        :param color: color of the tile (None for config default)
        :param coordinates: the coordinates on which the tile should be added
        :return: Successful added matter; False: Unsuccessful
Ahmad Reza's avatar
Ahmad Reza committed
443
        """
444
445
446
        if isinstance(coordinates, int) or isinstance(coordinates, float):
            coordinates = (coordinates, color, 0.0)
            color = None
Ahmad Reza's avatar
Ahmad Reza committed
447

448
449
        elif len(coordinates) == 2:
            coordinates = (coordinates[0], coordinates[1], 0.0)
Ahmad Reza's avatar
Ahmad Reza committed
450

451
452
453
454
455
456
457
458
459
460
461
462
463
464
        if self.grid.are_valid_coordinates(coordinates):
            if coordinates not in self.tile_map_coordinates:
                if color is None:
                    color = self.config_data.tile_color
                self.new_tile = tile.Tile(self, coordinates, color)
                self.tiles.append(self.new_tile)
                if self.vis is not None:
                    self.vis.tile_changed(self.new_tile)
                self.csv_round.update_tiles_num(len(self.tiles))
                self.tile_map_coordinates[self.new_tile.coordinates] = self.new_tile
                self.tile_map_id[self.new_tile.get_id()] = self.new_tile
                logging.info("Created tile with tile id %s on coordinates %s",
                             str(self.new_tile.get_id()), str(coordinates))
                return self.new_tile
Ahmad Reza's avatar
Ahmad Reza committed
465
466

            else:
467
468
                logging.info("there is already a tile on %s " % str(coordinates))
                return False
Ahmad Reza's avatar
Ahmad Reza committed
469
        else:
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
            logging.info("%s is not a valid location!" % str(coordinates))
            return False

    def remove_tile(self, tile_id):
        """
        Removes a tile with a given tile_id from to the world database

        :param tile_id: The tiles id that should be removed
        :return:  True: Successful removed; False: Unsuccessful
        """
        if tile_id in self.tile_map_id:
            rm_tile = self.tile_map_id[tile_id]
            self.tiles.remove(rm_tile)
            self.tiles_rm.append(rm_tile)
            if self.vis is not None:
                self.vis.remove_tile(rm_tile)
            logging.info("Deleted tile with tile id %s on %s", str(rm_tile.get_id()), str(rm_tile.coordinates))
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
                del self.tile_map_id[rm_tile.get_id()]
            except KeyError:
                pass
            try:  # cher: added so the program does not crashed if it does not find any entries in the map
                del self.tile_map_coordinates[rm_tile.coordinates]
            except KeyError:
                pass
            self.csv_round.update_tiles_num(len(self.tiles))
            self.csv_round.update_metrics(tile_deleted=1)
            self.__tile_deleted = True
498
499
500
            return True
        else:
            return False
Ahmad Reza's avatar
Ahmad Reza committed
501

502
    def remove_tile_on(self, coordinates):
Ahmad Reza's avatar
Ahmad Reza committed
503
        """
504
        Removes a tile on a give coordinates from to the world database
Ahmad Reza's avatar
Ahmad Reza committed
505

506
507
        :param coordinates: A tuple that includes the x and y coordinates
        :return: True: Successful removed; False: Unsuccessful
Ahmad Reza's avatar
Ahmad Reza committed
508
        """
509
510
        if coordinates in self.tile_map_coordinates:
            return self.remove_tile(self.tile_map_coordinates[coordinates].get_id())
Ahmad Reza's avatar
Ahmad Reza committed
511
512
513
514

        else:
            return False

515
    def add_location(self, coordinates, color=None):
Ahmad Reza's avatar
Ahmad Reza committed
516
        """
517
        Add a tile to the world database
Ahmad Reza's avatar
Ahmad Reza committed
518

519
520
521
        :param color:
        :param coordinates: the coordinates on which the tile should be added
        :return: True: Successful added; False: Unsuccessful
Ahmad Reza's avatar
Ahmad Reza committed
522
523
        """

524
525
526
        if isinstance(coordinates, int) or isinstance(coordinates, float):
            coordinates = (coordinates, color, 0.0)
            color = None
Ahmad Reza's avatar
Ahmad Reza committed
527

528
529
        elif len(coordinates) == 2:
            coordinates = (coordinates[0], coordinates[1], 0.0)
Ahmad Reza's avatar
Ahmad Reza committed
530

531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
        if self.grid.are_valid_coordinates(coordinates):
            if coordinates not in self.location_map_coordinates:
                if color is None:
                    color = self.config_data.location_color
                self.new_location = location.Location(self, coordinates, color)
                self.locations.append(self.new_location)
                if self.vis is not None:
                    self.vis.location_changed(self.new_location)
                self.location_map_coordinates[self.new_location.coordinates] = self.new_location
                self.location_map_id[self.new_location.get_id()] = self.new_location
                self.csv_round.update_locations_num(len(self.locations))
                logging.info("Created location with id %s on coordinates %s",
                             str(self.new_location.get_id()), str(self.new_location.coordinates))
                self.new_location.created = True
                return self.new_location
Ahmad Reza's avatar
Ahmad Reza committed
546
            else:
547
                logging.info("there is already a location on %s" % str(coordinates))
Ahmad Reza's avatar
Ahmad Reza committed
548
549
                return False
        else:
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
            logging.info("%s is not a valid location!" % str(coordinates))
            return False

    def remove_location(self, location_id):
        """
        Removes a tile with a given tile_id from to the world database

        :param location_id: The locations id that should be removed
        :return:  True: Successful removed; False: Unsuccessful
        """
        if location_id in self.location_map_id:
            rm_location = self.location_map_id[location_id]
            if rm_location in self.locations:
                self.locations.remove(rm_location)
            if self.vis is not None:
                self.vis.remove_location(rm_location)
            self.locations_rm.append(rm_location)
            logging.info("Deleted location with location id %s on %s", str(location_id), str(rm_location.coordinates))
            try:
                del self.location_map_coordinates[rm_location.coordinates]
            except KeyError:
                pass
            try:
                del self.location_map_id[location_id]
            except KeyError:
                pass
            self.csv_round.update_locations_num(len(self.locations))
            self.csv_round.update_metrics(location_deleted=1)
            self.__location_deleted = True
Karol Actun's avatar
Karol Actun committed
579
            return True
Ahmad Reza's avatar
Ahmad Reza committed
580
        else:
581
            return False
Ahmad Reza's avatar
Ahmad Reza committed
582

583
    def remove_location_on(self, coordinates):
Ahmad Reza's avatar
Ahmad Reza committed
584
        """
585
        Removes a location on a give coordinates from to the world database
Ahmad Reza's avatar
Ahmad Reza committed
586

587
588
        :param coordinates: A tuple that includes the x and y coordinates
        :return: True: Successful removed; False: Unsuccessful
Ahmad Reza's avatar
Ahmad Reza committed
589
        """
590
591
        if coordinates in self.location_map_coordinates:
            return self.remove_location(self.location_map_coordinates[coordinates].get_id())
Ahmad Reza's avatar
Ahmad Reza committed
592
        else:
Karol Actun's avatar
Karol Actun committed
593
            return False