Statistics
| Revision:

gvsig-vectorediting / org.gvsig.vectorediting / trunk / org.gvsig.vectorediting / org.gvsig.vectorediting.lib / org.gvsig.vectorediting.lib.prov / org.gvsig.vectorediting.lib.prov.polarmatrix / src / main / java / org / gvsig / vectorediting / lib / prov / polarmatrix / PolarMatrixEditingProvider.java @ 2616

History | View | Annotate | Download (23.6 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright ? 2007-2015 gvSIG Association
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24

    
25
package org.gvsig.vectorediting.lib.prov.polarmatrix;
26

    
27
import java.awt.geom.AffineTransform;
28
import java.util.ArrayList;
29
import java.util.HashMap;
30
import java.util.LinkedHashMap;
31
import java.util.List;
32
import java.util.Map;
33
import org.gvsig.fmap.dal.exception.DataException;
34
import org.gvsig.fmap.dal.feature.EditableFeature;
35
import org.gvsig.fmap.dal.feature.Feature;
36
import org.gvsig.fmap.dal.feature.FeatureSelection;
37
import org.gvsig.fmap.dal.feature.FeatureStore;
38
import org.gvsig.fmap.geom.Geometry;
39
import org.gvsig.fmap.geom.GeometryLocator;
40
import org.gvsig.fmap.geom.GeometryManager;
41
import org.gvsig.fmap.geom.GeometryUtils;
42
import org.gvsig.fmap.geom.aggregate.Aggregate;
43
import org.gvsig.fmap.geom.aggregate.MultiCurve;
44
import org.gvsig.fmap.geom.aggregate.MultiPoint;
45
import org.gvsig.fmap.geom.aggregate.MultiSurface;
46
import org.gvsig.fmap.geom.exception.CreateGeometryException;
47
import org.gvsig.fmap.geom.operation.GeometryOperationException;
48
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException;
49
import org.gvsig.fmap.geom.primitive.Arc;
50
import org.gvsig.fmap.geom.primitive.Curve;
51
import org.gvsig.fmap.geom.primitive.Line;
52
import org.gvsig.fmap.geom.primitive.Point;
53
import org.gvsig.fmap.geom.primitive.Surface;
54
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
55
import org.gvsig.symbology.SymbologyLocator;
56
import org.gvsig.symbology.SymbologyManager;
57
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.text.ISimpleTextSymbol;
58
import org.gvsig.tools.ToolsLocator;
59
import org.gvsig.tools.dispose.DisposableIterator;
60
import org.gvsig.tools.dispose.DisposeUtils;
61
import org.gvsig.tools.dynobject.DynObject;
62
import org.gvsig.tools.exception.BaseException;
63
import org.gvsig.tools.i18n.I18nManager;
64
import org.gvsig.tools.service.spi.ProviderServices;
65
import org.gvsig.vectorediting.lib.api.DrawingStatus;
66
import org.gvsig.vectorediting.lib.api.EditingServiceParameter;
67
import org.gvsig.vectorediting.lib.api.exceptions.DrawServiceException;
68
import org.gvsig.vectorediting.lib.api.exceptions.FinishServiceException;
69
import org.gvsig.vectorediting.lib.api.exceptions.InvalidEntryException;
70
import org.gvsig.vectorediting.lib.api.exceptions.StartServiceException;
71
import org.gvsig.vectorediting.lib.api.exceptions.StopServiceException;
72
import org.gvsig.vectorediting.lib.spi.AbstractEditingProvider;
73
import org.gvsig.vectorediting.lib.spi.DefaultDrawingStatus;
74
import org.gvsig.vectorediting.lib.spi.DefaultEditingServiceParameter;
75
import org.gvsig.vectorediting.lib.spi.EditingProvider;
76
import org.gvsig.vectorediting.lib.spi.EditingProviderFactory;
77
import org.gvsig.vectorediting.lib.spi.EditingProviderLocator;
78
import org.gvsig.vectorediting.lib.spi.EditingProviderManager;
79
import org.gvsig.vectorediting.lib.spi.EditingProviderServices;
80
import org.slf4j.Logger;
81
import org.slf4j.LoggerFactory;
82

    
83
/**
84
 * @author llmarques
85
 *
86
 */
87
public class PolarMatrixEditingProvider extends AbstractEditingProvider
88
    implements EditingProvider {
89

    
90
    private static final Logger LOGGER = LoggerFactory.getLogger(PolarMatrixEditingProvider.class);
91

    
92
    private final FeatureStore featureStore;
93

    
94
    private Map<EditingServiceParameter, Object> values;
95

    
96
    private final EditingServiceParameter selection;
97

    
98
    private final EditingServiceParameter elementsNumber;
99

    
100
    private final EditingServiceParameter centerPoint;
101

    
102
    private final EditingServiceParameter elementAngle;
103

    
104
    private final EditingServiceParameter rotateElements;
105

    
106
    /**
107
     * Default constructor.
108
     *
109
     * @param services
110
     *            available services for this provider
111
     * @param parameters
112
     *            of this provider
113
     */
114
    public PolarMatrixEditingProvider(DynObject parameters,
115
        ProviderServices services) {
116
        super(services);
117

    
118
        this.featureStore =
119
            (FeatureStore) parameters
120
                .getDynValue(EditingProviderFactory.FEATURE_STORE_FIELD);
121

    
122
        this.selection =
123
            new DefaultEditingServiceParameter("selection", "selection",
124
                EditingServiceParameter.TYPE.SELECTION);
125

    
126
        this.elementsNumber =
127
            new DefaultEditingServiceParameter("number_of_total_elements",
128
                "number_of_total_elements", EditingServiceParameter.TYPE.VALUE);
129

    
130
        I18nManager i18nManager = ToolsLocator.getI18nManager();
131
        Map<String, String> options = new LinkedHashMap<>();
132
        options.put(i18nManager.getTranslation("key_yes"), "yes");
133
        options.put(i18nManager.getTranslation("key_no"), "no");
134

    
135
        EditingProviderServices editingProviderServices =
136
            (EditingProviderServices) getProviderServices();
137

    
138
        this.rotateElements =
139
            new DefaultEditingServiceParameter("rotate_elements",
140
                editingProviderServices.makeConsoleMessage("rotate_elements",
141
                    options), options, EditingServiceParameter.TYPE.OPTION);
142

    
143
        this.centerPoint =
144
            new DefaultEditingServiceParameter("center", "center",
145
                EditingServiceParameter.TYPE.POSITION);
146

    
147
        this.elementAngle =
148
            new DefaultEditingServiceParameter("angle_between_elements",
149
                "angle_between_elements", EditingServiceParameter.TYPE.VALUE,
150
                EditingServiceParameter.TYPE.POSITION);
151
    }
152

    
153
    @Override
154
    public EditingServiceParameter next() {
155
        if (values.get(selection) == null) {
156
            return selection;
157
        } else if (values.get(elementsNumber) == null) {
158
            return elementsNumber;
159
        } else if (values.get(rotateElements) == null) {
160
            return rotateElements;
161
        } else if (values.get(centerPoint) == null) {
162
            return centerPoint;
163
        } else if (values.get(elementAngle) == null) {
164
            return elementAngle;
165
        }
166
        return null;
167
    }
168

    
169
    @Override
170
    public DrawingStatus getDrawingStatus(Point mousePosition)
171
        throws DrawServiceException {
172

    
173
        GeometryManager geometryManager = GeometryLocator.getGeometryManager();
174
        DefaultDrawingStatus drawingStatus = new DefaultDrawingStatus();
175
        EditingProviderServices editingProviderServices =
176
            (EditingProviderServices) getProviderServices();
177

    
178
        FeatureSelection featureSelection =
179
            (FeatureSelection) values.get(selection);
180
        Integer elementsNumberValue = (Integer) values.get(elementsNumber);
181
        Boolean rotateElementsValue = (Boolean) values.get(rotateElements);
182
        Point centerPointValue = (Point) values.get(centerPoint);
183

    
184
        if (featureSelection != null && elementsNumberValue != null
185
            && rotateElementsValue != null && centerPointValue != null) {
186

    
187
            Double angle = (Double) values.get(elementAngle);
188
            if (angle == null) {
189
                angle = GeometryUtils.calculateAngle(centerPointValue, mousePosition);
190
//                try {
191
//                    angle =
192
//                        editingProviderServices.getAngle(centerPointValue,
193
//                            mousePosition);
194
//                } catch (BaseException e) {
195
//                    throw new DrawServiceException(e);
196
//                }
197
            }
198

    
199
            // Creation of symbology
200
            EditingProviderManager editingProviderManager =
201
                EditingProviderLocator.getProviderManager();
202
            ISymbol auxiliaryPointSymbolEditing =
203
                editingProviderManager
204
                    .getSymbol("auxiliary-point-symbol-editing");
205
            ISymbol ruleAxisSymbol =
206
                editingProviderManager.getSymbol("rule-axis-symbol");
207
            ISymbol auxiliaryLineSymbolEditing =
208
                editingProviderManager
209
                    .getSymbol("auxiliary-line-symbol-editing");
210

    
211
            try {
212

    
213
                LOGGER.info("centerPoint: "+centerPointValue.toString());
214
                drawingStatus.addStatus(centerPointValue,
215
                    auxiliaryPointSymbolEditing, "");
216

    
217
                // Grade indicator creation
218
                int subtype = centerPointValue.getGeometryType().getSubType();
219
                double textDistance =
220
                    3 * centerPointValue.distance(mousePosition) / 4;
221
                Geometry pointText =
222
                    geometryManager.createPoint(
223
                        centerPointValue.getX() + textDistance
224
                            * Math.cos(angle / 2),
225
                        centerPointValue.getY() + textDistance
226
                            * Math.sin(angle / 2), subtype);
227
                LOGGER.info("pointText: "+pointText.toString());
228
                drawingStatus.addStatus(pointText, getTextSymbol(),
229
                    degToDms(Math.toDegrees(angle)));
230

    
231
                // Horizontal line of angle indicator creation
232
                Line horizontalLine = geometryManager.createLine(subtype);
233
                horizontalLine.setPoints(centerPointValue, geometryManager
234
                    .createPoint(
235
                        centerPointValue.getX()
236
                            + centerPointValue.distance(mousePosition),
237
                        centerPointValue.getY(), subtype));
238
                LOGGER.info("horizontalLine: "+horizontalLine.toString());
239
                drawingStatus.addStatus(horizontalLine, ruleAxisSymbol, "");
240

    
241
                // Vertical line of angle indicator creation
242
                Line verticalLine = geometryManager.createLine(subtype);
243
                verticalLine.setPoints(centerPointValue, mousePosition);
244
                LOGGER.info("verticalLine: "+verticalLine.toString());
245
                drawingStatus.addStatus(verticalLine, ruleAxisSymbol, "");
246

    
247
                // Arc angle indicator creation
248
//                Double ext = (2 * Math.PI) - angle;
249
//                Arc arc =
250
//                    editingProviderServices.createArc(centerPointValue,
251
//                        centerPointValue.distance(mousePosition) / 2, 0, ext,
252
//                        subtype);
253
                Arc arc = GeometryUtils.createArc(
254
                        centerPointValue, 
255
                        centerPointValue.distance(mousePosition) / 2, 
256
                        0, 
257
                        angle, 
258
                        subtype
259
                );
260
                    
261
                LOGGER.info("arc: "+arc.toString());
262
                drawingStatus.addStatus(arc, auxiliaryLineSymbolEditing, "");
263

    
264
            } catch (BaseException e) {
265
                throw new DrawServiceException(e);
266
            }
267

    
268
            DisposableIterator it = null;
269
            try {
270
                it = featureSelection.fastIterator();
271
                while (it.hasNext()) {
272
                    Feature feature = (Feature) it.next();
273
                    ISymbol previewSymbol = this.getPreviewSymbol(feature);
274
                    
275
                    Geometry geometry = feature.getDefaultGeometry();
276
                    Geometry[] matrix =
277
                        createPolarMatrix(geometry, elementsNumberValue,
278
                            rotateElementsValue, centerPointValue, angle);
279

    
280
                    for (Geometry element : matrix) {
281
                        LOGGER.info("element: "+element.toString());
282
                        ISymbol symbol = getSymbol(element);
283
                        if (element instanceof Aggregate) {
284
                            int primitivesNumber = ((Aggregate) element).getPrimitivesNumber();
285
                            for (int j = 0; j < primitivesNumber; j++) {
286
                                LOGGER.info("primitive: "+j);
287
                                drawingStatus.addStatus(((Aggregate) element).getPrimitiveAt(j), symbol, "");
288
                                drawingStatus.addStatus(((Aggregate) element).getPrimitiveAt(j), previewSymbol, "");
289
                            }
290
                        } else {
291
                            drawingStatus.addStatus(element, symbol, "");
292
                            drawingStatus.addStatus(element, previewSymbol, "");
293
                        }
294
                    }
295
                }
296
            } catch (BaseException e) {
297
                throw new DrawServiceException(e);
298
            } finally {
299
                DisposeUtils.disposeQuietly(it);
300
            }
301
        }
302
        return drawingStatus;
303
    }
304

    
305
    private ISymbol getSymbol(Geometry geometry) {
306

    
307
        EditingProviderManager editingProviderManager =
308
            EditingProviderLocator.getProviderManager();
309
        ISymbol auxiliaryPointSymbolEditing =
310
            editingProviderManager.getSymbol("auxiliary-point-symbol-editing");
311
        ISymbol lineSymbolEditing =
312
            editingProviderManager.getSymbol("line-symbol-editing");
313
        ISymbol polygonSymbolEditing =
314
            editingProviderManager.getSymbol("polygon-symbol-editing");
315

    
316
        if (geometry instanceof Curve || geometry instanceof MultiCurve) {
317
            return lineSymbolEditing;
318
        } else if (geometry instanceof Surface
319
            || geometry instanceof MultiSurface) {
320
            return polygonSymbolEditing;
321
        } else if (geometry instanceof Point || geometry instanceof MultiPoint) {
322
            return auxiliaryPointSymbolEditing;
323
        }
324
        return null;
325
    }
326

    
327
    private ISimpleTextSymbol getTextSymbol() {
328
        SymbologyManager symbologyManager =
329
            SymbologyLocator.getSymbologyManager();
330
        ISimpleTextSymbol textSymbol =
331
            symbologyManager.createSimpleTextSymbol();
332
        textSymbol.setFontSize(10);
333
        return textSymbol;
334
    }
335

    
336
    private String degToDms(double deg) {
337
        int d = (int) Math.floor(deg);
338
        double minfloat = (deg - d) * 60;
339
        int m = (int) Math.floor(minfloat);
340
        double secfloat = (minfloat - m) * 60;
341
        int s = (int) Math.round(secfloat);
342
        // After rounding, the seconds might become 60. These two
343
        // if-tests are not necessary if no rounding is done.
344
        if (s == 60) {
345
            m++;
346
            s = 0;
347
        }
348
        if (m == 60) {
349
            d++;
350
            m = 0;
351
        }
352
        return ("" + d + "\u00B0" + m + "\u2032" + s + "\u2033");
353
    }
354

    
355
    private Geometry[] createPolarMatrix(Geometry geometry,
356
        Integer elementsNumberValue, Boolean rotateElementsValue,
357
        Point centerPointValue, Double angle)
358
        throws GeometryOperationNotSupportedException,
359
        GeometryOperationException, CreateGeometryException {
360

    
361
        Geometry[] geometryMatrix = new Geometry[elementsNumberValue];
362

    
363
        for (int i = 0; i < elementsNumberValue; i++) {
364

    
365
            AffineTransform at = new AffineTransform();
366
            if (rotateElementsValue) {
367
                at = getRotateAffineTransform(centerPointValue, angle * i);
368
            } else {
369
                at = getRotateAffineTransform(centerPointValue, angle * i);
370
                
371
                double centerX = geometry.getEnvelope().getCenter(0);
372
                double centerY = geometry.getEnvelope().getCenter(1);
373
                GeometryManager geometryManager =
374
                    GeometryLocator.getGeometryManager();
375
                Point geometryCenter =
376
                    geometryManager.createPoint(centerX, centerY, GEOM2D);
377
                
378
                Point geometryCenterCloned = (Point) geometryCenter.cloneGeometry();
379
                geometryCenterCloned.transform(at);
380
                at = getMoveAffineTransform(geometryCenter, geometryCenterCloned);
381
            }
382

    
383
            Geometry clonedGeometry = geometry.cloneGeometry();
384
            clonedGeometry.transform(at);
385
            geometryMatrix[i] = clonedGeometry;
386
        }
387

    
388
        return geometryMatrix;
389
    }
390

    
391
    @Override
392
    public void stop() throws StopServiceException {
393
        values.clear();
394
    }
395

    
396
    @Override
397
    public List<EditingServiceParameter> getParameters() {
398
        List<EditingServiceParameter> parameters =
399
            new ArrayList<>();
400
        parameters.add(selection);
401
        parameters.add(rotateElements);
402
        parameters.add(centerPoint);
403
        parameters.add(elementAngle);
404
        return parameters;
405
    }
406

    
407
    @Override
408
    public void setValue(EditingServiceParameter parameter, Object value) throws InvalidEntryException {
409
        validateAndInsertValue(parameter, value);
410
    }
411

    
412
    @Override
413
    public void setValue(Object value) throws InvalidEntryException {
414
        EditingServiceParameter parameter = next();
415
        validateAndInsertValue(parameter, value);
416
    }
417

    
418
    private void validateAndInsertValue(EditingServiceParameter parameter,
419
        Object value) throws InvalidEntryException {
420

    
421
        boolean insertedValue = false;
422

    
423
        if (parameter.equals(selection) && value instanceof FeatureSelection) {
424
            FeatureSelection featureSelection = (FeatureSelection) value;
425

    
426
            if (featureSelection.getSelectedCount() > 0) {
427
                values.put(parameter, featureSelection);
428
                insertedValue = true;
429
            }
430

    
431
        } else if (parameter.equals(elementsNumber) && value instanceof Double) {
432

    
433
            if (value instanceof Double) {
434
                Double number = (Double) value;
435
                values.put(parameter, number.intValue());
436
                insertedValue = true;
437
            }
438

    
439
        } else if (parameter.equals(rotateElements) && value instanceof String) {
440

    
441
            String option = (String) value;
442
            I18nManager i18nManager = ToolsLocator.getI18nManager();
443
            if (option.equalsIgnoreCase(i18nManager.getTranslation("key_yes"))) {
444
                values.put(rotateElements, true);
445
                insertedValue = true;
446
            } else if (option.equalsIgnoreCase(i18nManager
447
                .getTranslation("key_no"))) {
448
                values.put(rotateElements, false);
449
                insertedValue = true;
450
            }
451

    
452
        } else if (parameter.equals(centerPoint) && value instanceof Point) {
453

    
454
            values.put(centerPoint, value);
455
            insertedValue = true;
456

    
457
        } else if (parameter.equals(elementAngle)) {
458

    
459
            if (value instanceof Double) {
460
                LOGGER.info("value Double:"+value.toString());
461

    
462
                values.put(elementAngle, Math.toRadians((Double) value));
463
                insertedValue = true;
464

    
465
            } else if (value instanceof Point) {
466
                LOGGER.info("value Point:"+value.toString());
467
                Point centerPointValue = (Point) values.get(centerPoint);
468
                Point valuePoint = (Point) value;
469
                    double angle = GeometryUtils.calculateAngle(centerPointValue, valuePoint);
470
                    values.put(elementAngle, angle);
471
                    insertedValue = true;
472
//                try {
473
//                    double angle = 
474
//                        editingProviderServices.getAngle(centerPointValue,
475
//                            valuePoint);
476
//                } catch (BaseException e) {
477
//                    throw new InvalidEntryException(e);
478
//                }
479
            }
480
        }
481

    
482
        if (!insertedValue) {
483
            throw new InvalidEntryException(null);
484
        }
485
    }
486

    
487
    @Override
488
    public Geometry finish() throws FinishServiceException {
489
        return null;
490
    }
491

    
492
    @Override
493
    public void finishAndStore() throws FinishServiceException {
494

    
495
        final EditingProviderServices editingProviderServices =
496
            (EditingProviderServices) getProviderServices();
497

    
498
        FeatureSelection featureSelection =
499
            (FeatureSelection) values.get(selection);
500
        final Integer elementsNumberValue =
501
            (Integer) values.get(elementsNumber);
502
        final Boolean rotateElementsValue =
503
            (Boolean) values.get(rotateElements);
504
        final Point centerPointValue = (Point) values.get(centerPoint);
505
        final Double elementAngleValue = (Double) values.get(elementAngle);
506

    
507
        if (featureSelection != null && elementsNumberValue != null
508
            && rotateElementsValue != null && centerPointValue != null
509
            && elementAngleValue != null) {
510

    
511
            try {
512
                featureSelection.accept((Object obj) -> {
513
                    Feature feature = (Feature) obj;
514
                    Geometry geometry = feature.getDefaultGeometry();
515
                    LOGGER.info("elementAngleValue:"+elementAngleValue);
516
                    
517
                    Geometry[] matrix =
518
                            createPolarMatrix(geometry, elementsNumberValue,
519
                                    rotateElementsValue, centerPointValue,
520
                                    elementAngleValue);
521
                    
522
                    for (Geometry matrix1 : matrix) {
523
                        EditableFeature eFeature =
524
                                editingProviderServices
525
                                        .getFeatureCopyWithoutUniqueIndex(featureStore,
526
                                                feature);
527
                        eFeature.setDefaultGeometry(matrix1);
528
                        editingProviderServices
529
                                .insertFeatureIntoFeatureStore(eFeature,
530
                                        featureStore);
531
                    }
532
                });
533
            } catch (BaseException e) {
534
                throw new FinishServiceException(e);
535
            }
536
        }
537
    }
538

    
539
    @Override
540
    public void start() throws StartServiceException, InvalidEntryException {
541
        values = new HashMap<>();
542
        FeatureSelection selected = null;
543
        if (featureStore != null && values.get(selection) == null) {
544
            try {
545
                selected = featureStore.getFeatureSelection();
546
            } catch (DataException e) {
547
                throw new StartServiceException(e);
548
            }
549
            if (selected.getSelectedCount() > 0) {
550
                try {
551
                    setValue(selected);
552
                } catch (InvalidEntryException e) {
553
                    throw new InvalidEntryException(e);
554
                }
555
            }
556
        }
557
    }
558

    
559
    @Override
560
    public String getName() {
561
        return PolarMatrixEditingProviderFactory.PROVIDER_NAME;
562
    }
563

    
564
    private AffineTransform getMoveAffineTransform(Point p1, Point p2)
565
        throws GeometryOperationNotSupportedException,
566
        GeometryOperationException {
567

    
568
        AffineTransform translate =
569
            AffineTransform.getTranslateInstance(p2.getX() - p1.getX(),
570
                p2.getY() - p1.getY());
571

    
572
        return translate;
573
    }
574

    
575
    private AffineTransform getRotateAffineTransform(Point axisP1, Double angle)
576
        throws GeometryOperationNotSupportedException,
577
        GeometryOperationException {
578

    
579
        AffineTransform translate =
580
            AffineTransform
581
                .getTranslateInstance(-axisP1.getX(), -axisP1.getY());
582

    
583
        AffineTransform rotate = AffineTransform.getRotateInstance(angle);
584

    
585
        AffineTransform inverseTranslate =
586
            AffineTransform.getTranslateInstance(axisP1.getX(), axisP1.getY());
587
        AffineTransform at = new AffineTransform(translate);
588

    
589
        at.preConcatenate(rotate);
590
        at.preConcatenate(inverseTranslate);
591
        return at;
592
    }
593
    @Override
594
    public Object getValue(EditingServiceParameter parameter) {
595
        return values!=null?values.get(parameter):null;
596
    }
597
}