Completed
Push — master ( 765f92...8230fd )
by Matt
18s queued 12s
created

Wander::getGeoJson()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
namespace App\Entity;
4
5
use ApiPlatform\Core\Annotation\ApiProperty;
6
use App\Repository\WanderRepository;
7
use ApiPlatform\Core\Annotation\ApiResource;
8
use ApiPlatform\Core\Annotation\ApiSubresource;
9
use DateInterval;
10
use DateTime;
11
use Doctrine\Common\Collections\ArrayCollection;
12
use Doctrine\Common\Collections\Collection;
13
use Symfony\Component\Serializer\Annotation\Groups;
14
use Doctrine\ORM\Mapping as ORM;
15
use App\EventListener\WanderUploadListener;
16
use Carbon\Carbon;
17
use Carbon\CarbonInterval;
18
use Doctrine\Common\Collections\Criteria;
19
20
use Doctrine\ORM\Mapping\Table;
21
use Doctrine\ORM\Mapping\Index;
22
23
24
/**
25
 * @ORM\Entity(repositoryClass=WanderRepository::class)
26
 *
27
 * @Table(indexes={@Index(name="ix_wander_start_time", columns={"start_time"})})
28
 *
29
 * @ORM\EntityListeners({
30
 *  WanderUploadListener::class, App\EventListener\WanderDeleteListener::class
31
 * })
32
 *
33
 * @ApiResource(
34
 *  collectionOperations={"get"={"normalization_context"={"groups"="wander:list"}}},
35
 *  itemOperations={"get"={"normalization_context"={"groups"="wander:item"}}},
36
 *  order={"startTime"="ASC"}
37
 * )
38
 * @ORM\HasLifecycleCallbacks()
39
 *
40
 */
41
class Wander
42
{
43
    /**
44
     * @ORM\Id
45
     * @ORM\GeneratedValue
46
     * @ORM\Column(type="integer")
47
     *
48
     * @Groups({"wander:list", "wander:item"})
49
     */
50
    private $id;
51
52
    /**
53
     * @ORM\Column(type="string", length=1024)
54
     *
55
     * @Groups({"wander:list", "wander:item"})
56
     */
57
    private $title;
58
59
    /**
60
     * @ORM\Column(type="datetime")
61
     *
62
     * @Groups({"wander:list", "wander:item"})
63
     */
64
    private $startTime;
65
66
    /**
67
     * @ORM\Column(type="datetime")
68
     *
69
     * @Groups({"wander:list", "wander:item"})
70
     */
71
    private $endTime;
72
73
    /**
74
     * @ORM\Column(type="text", nullable=true)
75
     *
76
     * @Groups({"wander:list", "wander:item"})
77
     */
78
    private $description;
79
80
    /**
81
     * @ORM\Column(type="string", length=255)
82
     *
83
     * @Groups({"wander:list", "wander:item"})
84
     */
85
    private $gpxFilename;
86
87
    /**
88
     * @ORM\OneToMany(targetEntity=Image::class, mappedBy="wander", cascade={"persist"})
89
     * @ORM\OrderBy({ "capturedAt" = "ASC", "id" = "ASC" })
90
     *
91
     * @Groups({"wander:item"})
92
     * @ApiSubresource
93
     * @ ApiProperty(attributes={"fetchEager": true})
94
     */
95
    private $images;
96
97
    /**
98
     * @ORM\Column(type="float", nullable=true)
99
     *
100
     * Distance walked, in metres.
101
     *
102
     */
103
    private $distance;
104
105
    /**
106
     * @ORM\Column(type="float", nullable=true)
107
     */
108
    private $avgSpeed;
109
110
    /**
111
     * @ORM\Column(type="float", nullable=true)
112
     */
113
    private $avgPace;
114
115
    /**
116
     * @ORM\Column(type="float", nullable=true)
117
     */
118
    private $minAltitude;
119
120
    /**
121
     * @ORM\Column(type="float", nullable=true)
122
     */
123
    private $maxAltitude;
124
125
    /**
126
     * @ORM\Column(type="float", nullable=true)
127
     */
128
    private $cumulativeElevationGain;
129
130
    /**
131
     * @var string|null
132
     *
133
     * @ApiProperty(iri="http://schema.org/contentUrl")
134
     * @Groups({"wander:list", "wander:item"})
135
     */
136
    public $contentUrl;
137
138
    /**
139
     * @ORM\Column(type="array", nullable=true)
140
     */
141
    private $centroid = [];
142
143
    /**
144
     * @ORM\Column(type="float", nullable=true)
145
     */
146
    private $angleFromHome;
147
148
    public function __construct()
149
    {
150
        $this->images = new ArrayCollection();
151
    }
152
153
    public function getId(): ?int
154
    {
155
        return $this->id;
156
    }
157
158
    public function getTitle(): ?string
159
    {
160
        return $this->title;
161
    }
162
163
    public function setTitle(string $title): self
164
    {
165
        $this->title = $title;
166
167
        return $this;
168
    }
169
170
    public function getStartTime(): ?\DateTimeInterface
171
    {
172
        return $this->startTime;
173
    }
174
175
    public function setStartTime(\DateTimeInterface $startTime): self
176
    {
177
        $this->startTime = $startTime;
178
179
        return $this;
180
    }
181
182
    public function getEndTime(): ?\DateTimeInterface
183
    {
184
        return $this->endTime;
185
    }
186
187
    public function setEndTime(\DateTimeInterface $endTime): self
188
    {
189
        $this->endTime = $endTime;
190
191
        return $this;
192
    }
193
194
    public function getDescription(): ?string
195
    {
196
        return $this->description;
197
    }
198
199
    public function setDescription(?string $description): self
200
    {
201
        $this->description = $description;
202
203
        return $this;
204
    }
205
206
    public function getGpxFilename(): ?string
207
    {
208
        return $this->gpxFilename;
209
    }
210
211
    public function setGpxFilename(string $gpxFilename): self
212
    {
213
        $this->gpxFilename = $gpxFilename;
214
215
        return $this;
216
    }
217
218
    public function isTimeLengthSuspicious()
219
    {
220
        $interval = $this->startTime->diff($this->endTime, true);
221
        return $interval->h > 12;
222
    }
223
224
    /**
225
     * @return Collection|Image[]
226
     */
227
    public function getImages(): Collection
228
    {
229
        return $this->images;
230
    }
231
232
    /**
233
     * @return Collection|Image[]
234
     */
235
    public function getImagesWithNoTitle(): Collection
236
    {
237
        $criteria = Criteria::create()
238
            ->where(Criteria::expr()->isNull('title'));
239
        return $this->getImages()->matching($criteria);
240
    }
241
242
    /**
243
     * @return Collection|Image[]
244
     */
245
    public function getImagesWithNoLatLng(): Collection
246
    {
247
        $criteria = Criteria::create()
248
            ->where(Criteria::expr()->isNull('latlng'));
249
        return $this->getImages()->matching($criteria);
250
    }
251
252
    /**
253
     * @return Collection|Image[]
254
     */
255
    public function getImagesWithNoLocation(): Collection
256
    {
257
        $criteria = Criteria::create()
258
            ->where(Criteria::expr()->isNull('location'));
259
        return $this->getImages()->matching($criteria);
260
    }
261
262
    /**
263
     * @return Collection|Image[]
264
     */
265
    public function getImagesWithNoRating(): Collection
266
    {
267
        $criteria = Criteria::create()
268
            ->where(Criteria::expr()->isNull('rating'));
269
        return $this->getImages()->matching($criteria);
270
    }
271
272
    /**
273
     * @return Collection|Image[]
274
     */
275
    public function getImagesWithNoTags(): Collection
276
    {
277
        return $this->getImages()->filter(function($image) {
278
            return $image->getTags()->isEmpty();
279
        });
280
    }
281
282
    /**
283
     * @return Collection|Image[]
284
     */
285
    public function getImagesWithNoAutoTags(): Collection
286
    {
287
        return $this->getImages()->filter(function($image) {
288
            return $image->getAutoTagsCount() == 0;
289
        });
290
    }
291
292
    public function addImage(Image $image): self
293
    {
294
        if (!$this->images->contains($image)) {
295
            $this->images[] = $image;
296
            $image->setWander($this);
297
        }
298
299
        return $this;
300
    }
301
302
    public function removeImage(Image $image): self
303
    {
304
        $image->setWander(null);
305
        $this->images->removeElement($image);
306
307
        return $this;
308
    }
309
310
    /**
311
     * @ORM\PreRemove
312
     */
313
    public function removeAllImages(): self
314
    {
315
        $images = $this->images;
316
        foreach ($images as $image) {
317
            $this->removeImage($image);
318
        }
319
        return $this;
320
    }
321
322
    public function getDistance(): ?float
323
    {
324
        return $this->distance;
325
    }
326
327
    public function setDistance(?float $distance): self
328
    {
329
        $this->distance = $distance;
330
331
        return $this;
332
    }
333
334
    public function getAvgSpeed(): ?float
335
    {
336
        return $this->avgSpeed;
337
    }
338
339
    public function setAvgSpeed(?float $avgSpeed): self
340
    {
341
        $this->avgSpeed = $avgSpeed;
342
343
        return $this;
344
    }
345
346
    public function getAvgPace(): ?float
347
    {
348
        return $this->avgPace;
349
    }
350
351
    public function setAvgPace(?float $avgPace): self
352
    {
353
        $this->avgPace = $avgPace;
354
355
        return $this;
356
    }
357
358
    public function getMinAltitude(): ?float
359
    {
360
        return $this->minAltitude;
361
    }
362
363
    public function setMinAltitude(?float $minAltitude): self
364
    {
365
        $this->minAltitude = $minAltitude;
366
367
        return $this;
368
    }
369
370
    public function getMaxAltitude(): ?float
371
    {
372
        return $this->maxAltitude;
373
    }
374
375
    public function setMaxAltitude(?float $maxAltitude): self
376
    {
377
        $this->maxAltitude = $maxAltitude;
378
379
        return $this;
380
    }
381
382
    public function getCumulativeElevationGain(): ?float
383
    {
384
        return $this->cumulativeElevationGain;
385
    }
386
387
    public function setCumulativeElevationGain(?float $cumulativeElevationGain): self
388
    {
389
        $this->cumulativeElevationGain = $cumulativeElevationGain;
390
391
        return $this;
392
    }
393
394
    public function getDuration(): ?CarbonInterval
395
    {
396
        if (!isset($this->startTime, $this->endTime)) {
397
            return null;
398
        }
399
400
        $difference = CarbonInterval::instance($this->startTime->diff($this->endTime));
401
        return $difference;
402
    }
403
404
    /**
405
     * @return array<float>
406
     */
407
    public function getCentroid(): ?array
408
    {
409
        return $this->centroid;
410
    }
411
412
    /**
413
     * @param array<float> $centroid
414
     */
415
    public function setCentroid(?array $centroid): self
416
    {
417
        $this->centroid = $centroid;
418
419
        return $this;
420
    }
421
422
    // We could calculate the angle from the Centroid each time, but
423
    // for now I'm just going to store it to save time.
424
    public function getAngleFromHome(): ?float
425
    {
426
        return $this->angleFromHome;
427
    }
428
429
    public function setAngleFromHome(?float $angleFromHome): self
430
    {
431
        $this->angleFromHome = $angleFromHome;
432
433
        return $this;
434
    }
435
436
    // Utilities
437
438
    // TODO: This is more like display code. Perhaps this should be in
439
    // a Twig extension and we should stick to storing the angle in here.
440
441
    /**
442
     * @var array<int, string>
443
     */
444
    private static $compass = [
445
        0 => 'N',
446
        1 => 'NE',
447
        2 => 'NE',
448
        3 => 'E',
449
        4 => 'E',
450
        5 => 'SE',
451
        6 => 'SE',
452
        7 => 'S',
453
        8 => 'S',
454
        9 => 'SW',
455
        10 => 'SW',
456
        11 => 'W',
457
        12 => 'W',
458
        13 => 'NW',
459
        14 => 'NW',
460
        15 => 'N'
461
      ];
462
463
    /**
464
     * @ORM\OneToOne(targetEntity=Image::class, mappedBy="featuringWander", cascade={"persist"})
465
     * @Groups({"wander:item"})
466
     * @ApiSubresource
467
     * @ApiProperty(attributes={"fetchEager": true})
468
     */
469
    private $featuredImage;
470
471
    /**
472
     * @ORM\Column(type="text", nullable=true)
473
     *
474
     * @Groups({"wander:list", "wander:item"})
475
     *
476
     */
477
    private $googlePolyline;
478
479
    public function getSector(): ?string
480
    {
481
        if ($this->angleFromHome !== null) {
482
            if ($this->angleFromHome >= 0 && $this->angleFromHome < 360.0) {
483
                return self::$compass[floor($this->angleFromHome / 22.5)];
484
            }
485
        }
486
        return null;
487
    }
488
489
    public function getCentroidAsString(): ?string {
490
        if ($this->centroid !== null) {
491
            return implode(", ", $this->centroid);
492
        }
493
        return null;
494
    }
495
496
    // TODO: Put this in but didn't use it in the end; maybe I don't need it
497
    // as in Twig we can use wander.images.count anyway. Take out?
498
    public function getImageCount(): int
499
    {
500
        // https://stackoverflow.com/a/8511611/300836
501
        return $this->images->count();
502
    }
503
504
    public function __toString():string
505
    {
506
        $result = $this->title ?? '';
507
        if (isset($this->startTime)) {
508
            $result .= ' (' . $this->startTime->format('j M Y') . ')';
509
        }
510
        return $result;
511
    }
512
513
    public function getFeaturedImage(): ?Image
514
    {
515
        return $this->featuredImage;
516
    }
517
518
    public function hasFeaturedImage(): bool
519
    {
520
        return $this->featuredImage !== null;
521
    }
522
523
    public function setFeaturedImage(?Image $featuredImage): self
524
    {
525
        // unset the owning side of the relation if necessary
526
        if ($this->featuredImage !== null) {
527
            $this->featuredImage->setFeaturingWander(null);
528
        }
529
530
        // set the owning side of the relation if necessary
531
        if ($featuredImage !== null && $featuredImage->getFeaturingWander() !== $this) {
532
            $featuredImage->setFeaturingWander($this);
533
        }
534
535
        $this->featuredImage = $featuredImage;
536
537
        return $this;
538
    }
539
540
    public function getGooglePolyline(): ?string
541
    {
542
        return $this->googlePolyline;
543
    }
544
545
    public function setGooglePolyline(?string $googlePolyline): self
546
    {
547
        $this->googlePolyline = $googlePolyline;
548
549
        return $this;
550
    }
551
}
552