WebFeatureService::DescribeFeatureType()   B
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 77
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 4
eloc 56
c 1
b 0
f 1
nc 4
nop 1
dl 0
loc 77
rs 8.9599

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Smindel\GIS\Control;
4
5
use DOMDocument;
6
use Smindel\GIS\GIS;
7
use ReflectionClass;
8
use SimpleXMLElement;
9
10
class WebFeatureService extends AbstractGISWebServiceController
11
{
12
    private static $url_handlers = [
13
        '$Model' => 'handleAction',
14
    ];
15
16
    private static $type_map = [
17
        'Varchar' => 'string',
18
        'Float' => 'decimal',       // key tbc
19
        'Datetime' => 'dateTime',   // key tbc
20
        'Int' => 'integer',         // key tbc
21
    ];
22
23
    private static $ns = 'ssgis=https://github.com/smindel/silverstripe-gis';
24
25
    public function getConfig($model)
26
    {
27
        $modelConfig = parent::getConfig($model);
28
        if (!$modelConfig) {
29
            return false;
30
        }
31
        $defaults = [
32
            'property_map' => singleton($model)->summaryFields(),
33
            'feature_type_name' => (new ReflectionClass($model))->getShortName(),
34
        ];
35
        return is_array($modelConfig) ? array_merge($defaults, $modelConfig) : $defaults;
36
    }
37
38
    public function index($request)
39
    {
40
        $operation = $request->requestVars()['request'] ?? (new SimpleXMLElement($raw = $request->getBody()))->getName();
41
42
        if (!in_array($operation, ['GetCapabilities', 'DescribeFeatureType', 'GetFeature'])) {
43
            throw new Exception(sprintf('Unkown operation "%s" requested', $operation));
0 ignored issues
show
Bug introduced by
The type Smindel\GIS\Control\Exception was not found. Did you mean Exception? If so, make sure to prefix the type with \.
Loading history...
44
        }
45
46
        return $this->$operation($request);
47
    }
48
49
    public function DescribeFeatureType($request)
50
    {
51
        $model = $this->getModel($request);
52
        $config = $this->getConfig($model);
53
        $propertyMap = $config['property_map'];
54
55
        if (count(array_intersect_key($params = array_intersect_key($request->requestVars(), array_fill_keys([
56
            'service',
57
            'version',
58
            'request',
59
            'typeNames',
60
            'exceptions',
61
            'outputFormat',
62
        ], null)), array_fill_keys([
63
            'service',
64
            'version',
65
            'request',
66
            'typeNames',
67
        ], null))) == 4) {
68
            extract($params);
69
        } else {
70
            $xml = new SimpleXMLElement($raw = $request->getBody());
71
            $service = (string)$xml['service'];
0 ignored issues
show
Unused Code introduced by
The assignment to $service is dead and can be removed.
Loading history...
72
            $version = (string)$xml['version'];
0 ignored issues
show
Unused Code introduced by
The assignment to $version is dead and can be removed.
Loading history...
73
            $request = $xml->getName();
0 ignored issues
show
Unused Code introduced by
The assignment to $request is dead and can be removed.
Loading history...
74
            $typeNames = [];
75
            foreach ($xml->xpath('TypeName') as $typeName) {
76
                $typeNames[] = (string)$typeName;
77
            }
78
        }
79
80
        list($nsName, $nsUri) = explode('=', $config['ns']);
81
82
        $dom = new DOMDocument('1.0','UTF-8');
83
        $schema = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'schema');
84
        $schema->setAttribute('elementFormDefault', 'qualified');
85
        $schema->setAttribute('targetNamespace', $nsUri);
86
        $schema->setAttribute('xmlns:gml', 'http://www.opengis.net/gml/3.2');
87
        $schema->setAttribute('xmlns:' . $nsName, $nsUri);
88
89
        $complexType = $dom->createElement('complexType');
90
        $complexType->setAttribute('name', $nsName . ':' . $config['feature_type_name']);
91
92
        $complexContent = $dom->createElement('complexContent');
93
94
        $extension = $dom->createElement('extension');
95
        $extension->setAttribute('base', 'gml:AbstractFeatureType');
96
97
        $sequence = $dom->createElement('sequence');
98
99
        foreach ($propertyMap as $fieldName => $propertyName) {
100
            $element = $dom->createElement('element');
101
            $element->setAttribute('name', $propertyName);
102
            $element->setAttribute('type', $this->config()->type_map[$model::config()->db[$fieldName]]);
103
            $sequence->appendChild($element);
104
        }
105
106
        $element = $dom->createElement('element');
107
        $element->setAttribute('name', $config['geometry_field']);
108
        $element->setAttribute('type', 'gml:GeometryPropertyType');
109
        $sequence->appendChild($element);
110
111
        $extension->appendChild($sequence);
112
113
        $complexContent->appendChild($extension);
114
115
        $complexType->appendChild($complexContent);
116
117
        $schema->appendChild($complexType);
118
119
        $dom->appendChild($schema);
120
121
        $response = $this->getResponse();
122
        $response->addHeader('content-type', 'text/xml; charset=utf-8');
123
        $response->setBody($dom->saveXML());
124
125
        return $response;
126
    }
127
128
    public function GetFeature($request)
129
    {
130
        $model = $this->getModel($request);
131
        $config = $this->getConfig($model);
132
        $list = $this->getRecords($request);
133
        $propertyMap = $config['property_map'];
134
135
        if (count(array_intersect_key($params = array_intersect_key($request->requestVars(), array_fill_keys([
136
            'service',
137
            'version',
138
            'request',
139
            'typeNames',
140
        ], null)), array_fill_keys([
141
            'service',
142
            'version',
143
            'request',
144
            'typeNames',
145
        ], null))) == 4) {
146
            extract($params);
147
        } else {
148
            $xml = new SimpleXMLElement($raw = $request->getBody());
149
            $service = (string)$xml['service'];
0 ignored issues
show
Unused Code introduced by
The assignment to $service is dead and can be removed.
Loading history...
150
            $version = (string)$xml['version'];
0 ignored issues
show
Unused Code introduced by
The assignment to $version is dead and can be removed.
Loading history...
151
            $request = $xml->getName();
0 ignored issues
show
Unused Code introduced by
The assignment to $request is dead and can be removed.
Loading history...
152
            $typeNames = [];
153
            foreach ($xml->xpath('TypeName') as $typeName) {
154
                $typeNames[] = (string)$typeName;
155
            }
156
        }
157
158
        list($nsName, $nsUri) = explode('=', $config['ns']);
159
160
        $dom = new DOMDocument('1.0','UTF-8');
161
162
        $featureCollection = $dom->createElement('FeatureCollection');
163
        $featureCollection->setAttribute('xmlns:gml', 'http://www.opengis.net/gml');
164
        $featureCollection->setAttribute('xmlns:' . $nsName, $nsUri);
165
166
        foreach($list as $item) {
167
            if (!$item->canView()) {
168
                continue;
169
            }
170
171
            $member = $dom->createElement('gml:featureMember');
172
173
            $record = $dom->createElement($nsName . ':' . $config['feature_type_name']);
174
            $record->setAttribute('gml:id', $item->ID);
175
176
            foreach ($propertyMap as $fieldName => $propertyName) {
177
                $property = $dom->createElement($propertyName, $item->$fieldName);
178
                $record->appendChild($property);
179
            }
180
181
            $Geometry = $dom->createElement($nsName . ':' . $config['geometry_field']);
182
183
            $geometry_field = $config['geometry_field'];
184
            $shape = $this->createGeometry($dom, $item->$geometry_field);
185
186
            $Geometry->appendChild($shape);
187
188
            $record->appendChild($Geometry);
189
190
            $member->appendChild($record);
191
192
            $featureCollection->appendChild($member);
193
        }
194
195
        $dom->appendChild($featureCollection);
196
197
        $response = $this->getResponse();
198
        $response->addHeader('content-type', 'text/xml; charset=utf-8');
199
        $response->setBody($dom->saveXML());
200
201
        return $response;
202
    }
203
204
    function createGeometry(DOMDocument $dom, $value)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
205
    {
206
        return call_user_func([$this, 'create' . ($gis = GIS::create($value))->type . 'Geometry'], $dom, $gis);
207
    }
208
209
    function createPointGeometry(DOMDocument $dom, GIS $gis)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
210
    {
211
        $point = $dom->createElement('gml:Point');
212
        $point->setAttribute('srsName', 'urn:ogc:def:crs:EPSG::4326');
213
214
        $pos = $dom->createElement('gml:pos', implode(' ', $gis->coordinates));
0 ignored issues
show
Bug introduced by
$gis->coordinates of type string is incompatible with the type array expected by parameter $pieces of implode(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

214
        $pos = $dom->createElement('gml:pos', implode(' ', /** @scrutinizer ignore-type */ $gis->coordinates));
Loading history...
215
        $point->appendChild($pos);
216
217
        return $point;
218
    }
219
220
    function createLineStringGeometry(DOMDocument $dom, GIS $gis)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
221
    {
222
        $line = $dom->createElement('gml:LineString');
223
        $line->setAttribute('srsName', 'urn:ogc:def:crs:EPSG::' . $gis->srid);
224
225
        $posList = $dom->createElement('gml:posList', implode(' ', array_map(function($point){return implode(' ', $point);}, $gis->coordinates)));
0 ignored issues
show
Bug introduced by
$gis->coordinates of type string is incompatible with the type array expected by parameter $array of array_map(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

225
        $posList = $dom->createElement('gml:posList', implode(' ', array_map(function($point){return implode(' ', $point);}, /** @scrutinizer ignore-type */ $gis->coordinates)));
Loading history...
226
227
        $line->appendChild($posList);
228
229
        return $line;
230
    }
231
232
    function createPolygonGeometry(DOMDocument $dom, GIS $gis)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
233
    {
234
        $polygon = $dom->createElement('gml:Polygon');
235
        $polygon->setAttribute('srsName', 'urn:ogc:def:crs:EPSG::' . $gis->srid);
236
237
        foreach ($gis->coordinates as $i => $ring) {
0 ignored issues
show
Bug introduced by
The expression $gis->coordinates of type string is not traversable.
Loading history...
238
            $exinterior = $dom->createElement('gml:' . ['exterior', 'interior'][$i]);
239
240
            $linearRing = $dom->createElement('gml:LinearRing');
241
242
            $posList = $dom->createElement('gml:posList', implode(' ', array_map(function($point){return implode(' ', $point);}, $ring)));
243
244
            $linearRing->appendChild($posList);
245
246
            $exinterior->appendChild($linearRing);
247
248
            $polygon->appendChild($exinterior);
249
        }
250
251
        return $polygon;
252
    }
253
}
254