Passed
Branch feature/2.1-geodispersion-dev (38d49e)
by Jonathan
04:17
created

GeoAnalysisMapAdapter::geoAnalysisViewId()   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
/**
4
 * webtrees-lib: MyArtJaub library for webtrees
5
 *
6
 * @package MyArtJaub\Webtrees
7
 * @subpackage GeoDispersion
8
 * @author Jonathan Jaubart <[email protected]>
9
 * @copyright Copyright (c) 2021, Jonathan Jaubart
10
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License, version 3
11
 */
12
13
declare(strict_types=1);
14
15
namespace MyArtJaub\Webtrees\Module\GeoDispersion\Model;
16
17
use Brick\Geo\IO\GeoJSON\Feature;
18
use Fisharebest\Webtrees\I18N;
19
use Illuminate\Support\Collection;
20
use MyArtJaub\Webtrees\Common\GeoDispersion\GeoAnalysis\GeoAnalysisPlace;
21
use MyArtJaub\Webtrees\Common\GeoDispersion\GeoAnalysis\GeoAnalysisResult;
22
use MyArtJaub\Webtrees\Common\GeoDispersion\GeoAnalysis\GeoAnalysisResultItem;
23
use MyArtJaub\Webtrees\Contracts\GeoDispersion\MapDefinitionInterface;
24
use MyArtJaub\Webtrees\Contracts\GeoDispersion\MapViewConfigInterface;
25
use MyArtJaub\Webtrees\Contracts\GeoDispersion\PlaceMapperInterface;
26
27
/**
28
 * Adapter to convert the results of a geographical dispersion analysis to data usable by a Map View
29
 */
30
class GeoAnalysisMapAdapter
31
{
32
    private int $id;
33
    private int $view_id;
34
    private MapDefinitionInterface $map;
35
    private PlaceMapperInterface $place_mapper;
36
    private MapViewConfigInterface $config;
37
38
    /**
39
     * Constructor for GeoAnalysisMapAdapter
40
     *
41
     * @param int $id
42
     * @param MapDefinitionInterface $map
43
     * @param PlaceMapperInterface $mapper
44
     * @param MapViewConfigInterface $config
45
     */
46
    public function __construct(
47
        int $id,
48
        int $view_id,
49
        MapDefinitionInterface $map,
50
        PlaceMapperInterface $mapper,
51
        MapViewConfigInterface $config
52
    ) {
53
        $this->id = $id;
54
        $this->view_id = $view_id;
55
        $this->map = $map;
56
        $this->place_mapper = $mapper;
57
        $this->config = $config;
58
        $this->place_mapper->setConfig($this->config->mapperConfig());
59
        $this->place_mapper->setData('map', $map);
60
        $this->place_mapper->boot();
61
    }
62
63
    /**
64
     * Create a copy of the GeoAnalysisMapAdapter with new properties.
65
     *
66
     * @param MapDefinitionInterface $map
67
     * @param PlaceMapperInterface $mapper
68
     * @param string $mapping_property
69
     * @return self
70
     */
71
    public function with(
72
        MapDefinitionInterface $map,
73
        PlaceMapperInterface $mapper,
74
        string $mapping_property
75
    ): self {
76
        $new = clone $this;
77
        $new->map = $map;
78
        $new->place_mapper = $mapper;
79
        $new->config = $this->config->with($mapping_property, $mapper->config());
80
        return $new;
81
    }
82
83
    /**
84
     * Get the GeoAnalysisMapAdapter ID
85
     *
86
     * @return int
87
     */
88
    public function id(): int
89
    {
90
        return $this->id;
91
    }
92
93
    /**
94
     * Get the ID of the associated GeoAnalysisView
95
     *
96
     * @return int
97
     */
98
    public function geoAnalysisViewId(): int
99
    {
100
        return $this->view_id;
101
    }
102
103
    /**
104
     * Get the associated target map
105
     *
106
     * @return MapDefinitionInterface
107
     */
108
    public function map(): MapDefinitionInterface
109
    {
110
        return $this->map;
111
    }
112
113
    /**
114
     * Get the Place Mapper used for the mapping
115
     *
116
     * @return PlaceMapperInterface
117
     */
118
    public function placeMapper(): PlaceMapperInterface
119
    {
120
        return $this->place_mapper;
121
    }
122
123
    /**
124
     * Get the configuration of the Map View.
125
     *
126
     * @return MapViewConfigInterface
127
     */
128
    public function viewConfig(): MapViewConfigInterface
129
    {
130
        return $this->config;
131
    }
132
133
    /**
134
     * Convert the geographical analysis result to a MapAdapter result for usage in the Map View
135
     *
136
     * @param GeoAnalysisResult $result
137
     * @return MapAdapterResult
138
     */
139
    public function convert(GeoAnalysisResult $result): MapAdapterResult
140
    {
141
        $result = $result->copy();
142
143
        $features = [];
144
        list($features_data, $result) = $this->featureAnalysisData($result);
145
146
        $places_found = $result->countFound();
147
        foreach ($this->map->features() as $feature) {
148
            $feature_id = $this->featureId($feature);
149
            if ($feature_id !== null && $features_data->has($feature_id)) {
150
                /** @var MapFeatureAnalysisData $feature_data */
151
                $feature_data = $features_data->get($feature_id)->tagAsExisting();
152
                $place_count = $feature_data->count();
153
                $features[] = $feature
154
                    ->withProperty('count', $place_count)
155
                    ->withProperty('ratio', $places_found > 0 ? $place_count / $places_found : 0)
156
                    ->withProperty(
157
                        'places',
158
                        $feature_data->places()
159
                            ->map(fn(GeoAnalysisPlace $place): string => $place->place()->firstParts(1)->first())
160
                            ->sort(I18N::comparator())
161
                            ->toArray()
162
                    );
163
            } else {
164
                $features[] = $feature;
165
            }
166
        }
167
168
        $features_data
169
            ->filter(fn(MapFeatureAnalysisData $data) => !$data->existsInMap())
170
            ->each(
171
                fn (MapFeatureAnalysisData $data) =>
172
                    $data->places()->each(
173
                        fn(GeoAnalysisPlace $place) => $result->exclude($place)
174
                    )
175
            );
176
177
        return new MapAdapterResult($result, $features);
178
    }
179
180
    /**
181
     * Populate the map features with the mapped Places and total count
182
     *
183
     * @param GeoAnalysisResult $result
184
     * @return array
185
     */
186
    protected function featureAnalysisData(GeoAnalysisResult $result): array
187
    {
188
        $features_mapping = new Collection();
189
190
        $byplaces = $result->knownPlaces();
191
        $byplaces->each(function (GeoAnalysisResultItem $item) use ($features_mapping, $result): void {
192
            $id = $this->place_mapper->map($item->place()->place(), $this->config->mapMappingProperty());
193
194
            if ($id !== null && mb_strlen($id) > 0) {
195
                $features_mapping->put(
196
                    $id,
197
                    $features_mapping->get($id, new MapFeatureAnalysisData($id))->add($item->place(), $item->count())
198
                );
199
            } else {
200
                $result->exclude($item->place());
201
            }
202
        });
203
204
        return [ $features_mapping, $result];
205
    }
206
207
    /**
208
     * Get the value of the feature property used for the mapping
209
     *
210
     * @param Feature $feature
211
     * @return string|NULL
212
     */
213
    protected function featureId(Feature $feature): ?string
214
    {
215
        return $feature->getProperty($this->config->mapMappingProperty());
216
    }
217
}
218