Statistics
| Revision:

svn-gvsig-desktop / trunk / extensions / extJDBC / src / com / iver / cit / gvsig / fmap / drivers / jdbc / postgis / PostGIS.java @ 40034

History | View | Annotate | Download (16.6 KB)

1
/*
2
 * Created on 26-oct-2005
3
 *
4
 * gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
5
 *
6
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU General Public License
10
 * as published by the Free Software Foundation; either version 2
11
 * of the License, or (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21
 *
22
 * For more information, contact:
23
 *
24
 *  Generalitat Valenciana
25
 *   Conselleria d'Infraestructures i Transport
26
 *   Av. Blasco Ib??ez, 50
27
 *   46010 VALENCIA
28
 *   SPAIN
29
 *
30
 *      +34 963862235
31
 *   gvsig@gva.es
32
 *      www.gvsig.gva.es
33
 *
34
 *    or
35
 *
36
 *   IVER T.I. S.A
37
 *   Salamanca 50
38
 *   46005 Valencia
39
 *   Spain
40
 *
41
 *   +34 963163400
42
 *   dac@iver.es
43
 */
44
package com.iver.cit.gvsig.fmap.drivers.jdbc.postgis;
45

    
46
import java.io.ByteArrayOutputStream;
47
import java.io.PrintStream;
48
import java.io.UnsupportedEncodingException;
49
import java.nio.charset.Charset;
50
import java.sql.Date;
51
import java.sql.SQLException;
52
import java.sql.Types;
53

    
54
import com.hardcode.gdbms.engine.values.DateValue;
55
import com.hardcode.gdbms.engine.values.NullValue;
56
import com.hardcode.gdbms.engine.values.Value;
57
import com.hardcode.gdbms.engine.values.ValueWriter;
58
import com.iver.cit.gvsig.exceptions.visitors.ProcessVisitorException;
59
import com.iver.cit.gvsig.fmap.core.FPolygon2D;
60
import com.iver.cit.gvsig.fmap.core.FPolyline2D;
61
import com.iver.cit.gvsig.fmap.core.FShape;
62
import com.iver.cit.gvsig.fmap.core.FShape3D;
63
import com.iver.cit.gvsig.fmap.core.FShapeM;
64
import com.iver.cit.gvsig.fmap.core.GeneralPathX;
65
import com.iver.cit.gvsig.fmap.core.IFeature;
66
import com.iver.cit.gvsig.fmap.core.IGeometry;
67
import com.iver.cit.gvsig.fmap.core.IGeometry3D;
68
import com.iver.cit.gvsig.fmap.core.IGeometryM;
69
import com.iver.cit.gvsig.fmap.core.IRow;
70
import com.iver.cit.gvsig.fmap.core.ShapeFactory;
71
import com.iver.cit.gvsig.fmap.core.ShapeMFactory;
72
import com.iver.cit.gvsig.fmap.drivers.DBLayerDefinition;
73
import com.iver.cit.gvsig.fmap.drivers.DefaultJDBCDriver;
74
import com.iver.cit.gvsig.fmap.drivers.FieldDescription;
75
import com.iver.cit.gvsig.fmap.drivers.XTypes;
76
import com.iver.utiles.console.jedit.SQLTokenMarker;
77
import com.vividsolutions.jts.geom.Geometry;
78

    
79
/**
80
 * @author fjp
81
 * Necesitamos que esta clase no trabaje con funciones est?ticas
82
 * porque puede haber capas que provengan de distintas bases de datos.
83
 */
84
public class PostGIS {
85

    
86
        private String toEncode;
87

    
88
        /**
89
         * Mover esto a IverUtiles
90
         *
91
         * @param val
92
         * @return
93
         */
94
        public boolean isNumeric(Value val) {
95

    
96
                switch (val.getSQLType()) {
97
                case Types.DOUBLE:
98
                case Types.FLOAT:
99
                case Types.INTEGER:
100
                case Types.SMALLINT:
101
                case Types.BIGINT:
102
                case Types.NUMERIC:
103
                case Types.REAL:
104
                case Types.TINYINT:
105
                        return true;
106
                }
107

    
108
                return false;
109
        }
110

    
111
        /**
112
         * @param dbLayerDef
113
         * @param fieldsDescr
114
         * @param bCreateGID @DEPRECATED
115
         * @return
116
         */
117
        public String getSqlCreateSpatialTable(DBLayerDefinition dbLayerDef,
118
                        FieldDescription[] fieldsDescr, boolean bCreateGID) {
119

    
120
                String resul;
121
                /* boolean bExistGID = false;
122
                for (int i = 0; i < dbLayerDef.getFieldNames().length; i++) {
123
                        if (dbLayerDef.getFieldNames()[i].equalsIgnoreCase("gid")) {
124
                                bExistGID = true;
125
                                break;
126
                        }
127
                } */
128
                /* if (bExistGID) // Usamos el existente y no a?adimos ninguno nosotros
129
                        resul = "CREATE TABLE " + dbLayerDef.getTableName() + " (";
130
                else */
131
                // FJP: NUEVO: NO TOLERAMOS CAMPOS QUE SE LLAMEN GID. Lo reservamos para uso nuestro.
132
                resul = "CREATE TABLE " + dbLayerDef.getComposedTableName()
133
                                        + " ( " + "\""+dbLayerDef.getFieldID()+"\"" +" serial PRIMARY KEY ";
134
                int j=0;
135
                for (int i = 0; i < dbLayerDef.getFieldNames().length; i++) {
136
                        int fieldType = fieldsDescr[i].getFieldType();
137
                        String strType = XTypes.fieldTypeToString(fieldType);
138
                        /*
139
                         * if (fieldType == Types.VARCHAR) strType = strType + "(" +
140
                         * fieldsDescr[i].getFieldLength() + ")";
141
                         */
142
                        if (fieldsDescr[i].getFieldName().equalsIgnoreCase(dbLayerDef.getFieldID()))
143
                                continue;
144
                        resul = resul + ", " + "\""+dbLayerDef.getFieldNames()[i]+"\"" + " "        + strType;
145
                        j++;
146
                }
147
                resul = resul + ");";
148
                return resul;
149
        }
150

    
151
        public String getSqlAlterTable(DBLayerDefinition dbLayerDef) {
152
                String strGeometryFieldType;
153
                strGeometryFieldType = "GEOMETRY";
154

    
155
                switch (dbLayerDef.getShapeType()) {
156
                case FShape.POINT:
157
                        strGeometryFieldType = XTypes.fieldTypeToString(XTypes.POINT2D);
158
                        break;
159
                case FShape.LINE:
160
                        strGeometryFieldType = XTypes.fieldTypeToString(XTypes.LINE2D);
161
                        break;
162
                case FShape.POLYGON:
163
                        strGeometryFieldType = XTypes.fieldTypeToString(XTypes.POLYGON2D);
164
                        break;
165
                case FShape.MULTI:
166
                        strGeometryFieldType = XTypes.fieldTypeToString(XTypes.MULTI2D);
167
                        break;
168
                case FShape.MULTIPOINT:
169
                        strGeometryFieldType = XTypes.fieldTypeToString(XTypes.MULTIPOINT);
170
                        break;
171
                }
172

    
173
                String schema = dbLayerDef.getSchema();
174
                if (schema == null || schema.equals("")){
175
                        schema = " current_schema()::Varchar ";
176
                } else {
177
                        schema = "'" +schema + "'";
178
                }
179

    
180
                String result = "SELECT AddGeometryColumn("
181
                                + schema + ", '"
182
                                + dbLayerDef.getTableName() + "', '"
183
                                + dbLayerDef.getFieldGeometry() + "', "
184
                                + DefaultJDBCDriver.removePrefix(dbLayerDef.getSRID_EPSG()) + ", '" + strGeometryFieldType + "', "
185
                                + dbLayerDef.getDimension() + ");";
186

    
187
                return result;
188
        }
189

    
190
        /**
191
         * From geotools Adds quotes to an object for storage in postgis. The object
192
         * should be a string or a number. To perform an insert strings need quotes
193
         * around them, and numbers work fine with quotes, so this method can be
194
         * called on unknown objects.
195
         *
196
         * @param value
197
         *            The object to add quotes to.
198
         *
199
         * @return a string representation of the object with quotes.
200
         */
201
        protected String addQuotes(Object value) {
202
                String retString;
203

    
204
                if (value != null) {
205
                        if (value instanceof NullValue)
206
                                retString = "null";
207
                        else
208
                                retString = "'" + doubleQuote(value) + "'";
209

    
210
                } else {
211
                        retString = "null";
212
                }
213

    
214
                return retString;
215
        }
216

    
217
        /**
218
         * FIXME: Maybe we don't need to test if encoding to the database is possible or not. This conversion may be slow.
219
         * But in the other hand, the user may be able to store his data and don't loose all the changes...
220
         * @param obj
221
         * @return
222
         */
223
        private String doubleQuote(Object obj) {
224
                String aux = obj.toString().replaceAll("'", "''");
225
                StringBuffer strBuf = new StringBuffer(aux);
226
                ByteArrayOutputStream out = new ByteArrayOutputStream(strBuf.length());
227
                String aux2 = "Encoding ERROR";
228

    
229
                try {
230
                        PrintStream printStream = new PrintStream(out, true, toEncode);
231
                        printStream.print(aux);                        
232
                        aux2 = out.toString(toEncode);
233
//                        System.out.println(aux + " " + aux2);
234
                } catch (UnsupportedEncodingException e) {
235
                        // TODO Auto-generated catch block
236
                        e.printStackTrace();
237
                }
238

    
239
                return aux2;
240
        }
241

    
242
        /**
243
         * Based in code from JUMP (VividSolutions) and Geotools Things to be aware:
244
         * We always will use Spatial Tables with Unique ID. IFeature has the same
245
         * field order than dbLayerDef.getFieldNames()
246
         *
247
         * @param dbLayerDef
248
         * @param feat
249
         * @return
250
         * @throws SQLException
251
         * @throws ProcessVisitorException
252
         */
253
        public String getSqlInsertFeature(DBLayerDefinition dbLayerDef,
254
                        IFeature feat) throws ProcessVisitorException {
255
                StringBuffer sqlBuf = new StringBuffer("INSERT INTO "
256
                                + dbLayerDef.getComposedTableName() + " (");
257
                String sql = null;
258
                int numAlphanumericFields = dbLayerDef.getFieldNames().length;
259

    
260
                for (int i = 0; i < numAlphanumericFields; i++) {
261
                        String name = dbLayerDef.getFieldsDesc()[i].getFieldName();
262
                        // if (cols.contains(name) && (!name.equals(uniqueCol) ||
263
                        // existsUnique)) {
264
                        if (name.equals(dbLayerDef.getFieldID()))
265
                                continue;
266
                        sqlBuf.append(" " + "\""+name+"\"" + ",");
267
                        // }
268
                }
269
                sqlBuf.append(" \"" + dbLayerDef.getFieldGeometry() + "\"");
270
                // sqlBuf.deleteCharAt(sqlBuf.lastIndexOf(","));
271
                sqlBuf.append(" ) VALUES (");
272
                String insertQueryHead = sqlBuf.toString();
273
                sqlBuf = new StringBuffer(insertQueryHead);
274
                for (int j = 0; j < numAlphanumericFields; j++) {
275
                        String name = dbLayerDef.getFieldsDesc()[j].getFieldName();
276
                        if (name.equals(dbLayerDef.getFieldID()))
277
                                continue;
278

    
279
                        if (isNumeric(feat.getAttribute(j))){
280
                                sqlBuf.append(feat.getAttribute(j) + ", ");
281
                        }else if(feat.getAttribute(j).getSQLType() == Types.DATE){
282
                                //If the field is a date, the driver can not use the client encoding.
283
                                //It uses the same encoding that the user has written on the table
284
                                sqlBuf.append(addQuotes(((DateValue)feat.getAttribute(j)).getValue().toString()) + ", ");
285
                        }else{
286
                                sqlBuf.append(addQuotes(feat.getAttribute(j)) + ", ");
287
                        }
288
                }
289
                IGeometry geometry=feat.getGeometry();
290
                int type=dbLayerDef.getShapeType();
291
                if (geometry.getGeometryType()!=type){
292
                        if (type==FShape.POLYGON){
293
                                geometry=ShapeFactory.createPolygon2D(new GeneralPathX(geometry.getInternalShape()));
294
                        }else if (type==FShape.LINE){
295
                                geometry=ShapeFactory.createPolyline2D(new GeneralPathX(geometry.getInternalShape()));
296
                        }else if (type==(FShape.POLYGON|FShape.Z)){
297
                                geometry=ShapeFactory.createPolygon3D(new GeneralPathX(geometry.getInternalShape()),((IGeometry3D)geometry).getZs());
298
                        }else if (type==(FShape.LINE|FShape.Z)){
299
                                geometry=ShapeFactory.createPolyline3D(new GeneralPathX(geometry.getInternalShape()),((IGeometry3D)geometry).getZs());
300
                        }else if (type==(FShape.LINE|FShape.M)){ //MCoord
301
                                geometry=ShapeMFactory.createPolyline2DM(new GeneralPathX(geometry.getInternalShape()),((IGeometryM)geometry).getMs()); //MCoord
302
                        }
303
                }
304
                
305
                if (!isCorrectGeometry(geometry, type))
306
                        throw new ProcessVisitorException("incorrect_geometry",new Exception());
307
                //MCoord
308
                if (((type & FShape.M) != 0) && ((type & FShape.MULTIPOINT) == 0)) {
309
                        sqlBuf.append(" ST_GeometryFromText( '"
310
                                        + ((FShapeM)geometry.getInternalShape()).toText() + "', "
311
                                        + DefaultJDBCDriver.removePrefix(dbLayerDef.getSRID_EPSG()) + ")");
312
                } else
313
                        //ZCoord
314
                        if ((type & FShape.Z) != 0) {
315
                                if ((type & FShape.MULTIPOINT) != 0) {
316
                                        //TODO: Metodo toText 3D o 2DM                                         
317
                                } else {
318
                                //Its not a multipoint
319
                                sqlBuf.append(" GeometryFromText( '"
320
                                                + ((FShape3D)feat.getGeometry().getInternalShape()).toText() + "', "
321
                                                + DefaultJDBCDriver.removePrefix(dbLayerDef.getSRID_EPSG()) + ")");
322
                                }
323

    
324
                        }        
325
                        //XYCoord
326
                        else {
327
                                Geometry jtsGeom=geometry.toJTSGeometry();
328
                                if (jtsGeom==null || !isCorrectType(jtsGeom, type)){
329
                                        throw new ProcessVisitorException("incorrect_geometry",new Exception());
330
                                }
331
                                
332
                
333
                        //If they layer is a 2D layer writing a 2D geometry will throw an error
334
                        //With st_force_3d it is avoid                
335
                        if (dbLayerDef.getDimension() == 3) sqlBuf.append("ST_Force_3D (");
336
                        
337
                        sqlBuf.append(" ST_GeometryFromText( '"
338
                                + jtsGeom.toText() + "', "
339
                                + DefaultJDBCDriver.removePrefix(dbLayerDef.getSRID_EPSG()) + ")");
340

    
341
                        if (dbLayerDef.getDimension() == 3) sqlBuf.append(")");                        
342
                }
343

    
344
                // sqlBuf.deleteCharAt(sqlBuf.lastIndexOf(","));
345
                sqlBuf.append(" ) ");
346
                sql = sqlBuf.toString();
347
                return sql;
348
        }
349

    
350
        private boolean isCorrectType(Geometry jtsGeom, int type) {
351
                if (FShape.POLYGON==type){
352
                        if (!jtsGeom.getGeometryType().equals("MultiPolygon") && !jtsGeom.getGeometryType().equals("Polygon") )
353
                                return false;
354
                }
355
                return true;
356
        }
357

    
358
        private boolean isCorrectGeometry(IGeometry geometry, int type) {
359
                if (FShape.POLYGON==type){
360
                        FPolygon2D polygon = (FPolygon2D)geometry.getInternalShape();
361
                        if (!(polygon.getBounds2D().getWidth()>0 && polygon.getBounds2D().getHeight()>0))
362
                                return false;
363
                }else if (FShape.LINE==type){
364
                        FPolyline2D line = (FPolyline2D)geometry.getInternalShape();
365
                        if (!(line.getBounds2D().getWidth()>0 || line.getBounds2D().getHeight()>0))
366
                                return false;
367
                }
368

    
369
                return true;
370
        }
371

    
372
        public String getSqlCreateIndex(DBLayerDefinition lyrDef) {
373
                String indexName = lyrDef.getTableName() + "_"
374
                                + lyrDef.getFieldGeometry() + "_gist";
375
                String sql = "CREATE INDEX \"" + indexName + "\" ON \""
376
                                + lyrDef.getComposedTableName() + "\" USING GIST (\""
377
                                + lyrDef.getFieldGeometry() + "\" GIST_GEOMETRY_OPS)";
378

    
379
                return sql;
380
        }
381

    
382
        public String getSqlModifyFeature(DBLayerDefinition dbLayerDef, IFeature feat) {
383
                /*
384
                         UPDATE weather
385
                         SET temp_hi = temp_hi - 2,  temp_lo = temp_lo - 2
386
                         WHERE date > '1994-11-28';
387
                 */
388
                StringBuffer sqlBuf = new StringBuffer("UPDATE "
389
                                + dbLayerDef.getComposedTableName() + " SET");
390
                String sql = null;
391
                int numAlphanumericFields = dbLayerDef.getFieldsDesc().length;
392

    
393
                for (int i = 0; i < numAlphanumericFields; i++) {
394
                        FieldDescription fldDesc = dbLayerDef.getFieldsDesc()[i];
395
                        if (fldDesc != null)
396
                        {
397
                                String name = fldDesc.getFieldName();
398
                                // El campo gid no lo actualizamos.
399
                                if (name.equalsIgnoreCase(dbLayerDef.getFieldID()))
400
                                        continue;
401
                                Value val = feat.getAttribute(i);
402
                                if (val != null)
403
                                {
404
                                        String strAux = val.getStringValue(ValueWriter.internalValueWriter);
405
                                        sqlBuf.append(" " + "\""+name+"\"" + " = " + strAux + " ,");
406
                                }
407
                        }
408
                }
409
                //If pos > 0 there is at least one field..
410
                int pos = sqlBuf.lastIndexOf(",");
411
                if (pos > -1){
412
                        sqlBuf.deleteCharAt(pos);
413
                }
414
                if (feat.getGeometry() != null)
415
                {
416
                        if (pos > -1){
417
                                sqlBuf.append(",");
418
                        }
419
                        sqlBuf.append(" \"" + dbLayerDef.getFieldGeometry() + "\"");
420
                        sqlBuf.append(" = ");
421
                        //MCoord
422
                        int type = feat.getGeometry().getGeometryType();
423
                        if (((type & FShape.M) != 0) && ((type & FShape.MULTIPOINT) == 0)) {
424
                                sqlBuf.append(" ST_GeometryFromText( '"
425
                                                + ((FShapeM)feat.getGeometry().getInternalShape()).toText() + "', "
426
                                                + DefaultJDBCDriver.removePrefix(dbLayerDef.getSRID_EPSG()) + ")");
427
                        } else
428
                                
429
                        //ZCoord
430
                                if ((type & FShape.Z) != 0) {
431
                                        if ((type & FShape.MULTIPOINT) != 0) {
432
                                                //TODO: Metodo toText 3D o 2DM                                                                                         
433
                                        } else {
434
                                        //Its not a multipoint
435
                                        sqlBuf.append(" ST_GeometryFromText( '"
436
                                                        + ((FShape3D)feat.getGeometry().getInternalShape()).toText() + "', "
437
                                                        + DefaultJDBCDriver.removePrefix(dbLayerDef.getSRID_EPSG()) + ")");
438
                                        }
439
                                        
440
                                }
441
                        
442
                        //XYCoord
443
                        else{
444
                                
445
                                //If they layer is a 2D layer writing a 2D geometry will throw an error
446
                                //With st_force_3d it is avoid                
447
                                if (dbLayerDef.getDimension() == 3) sqlBuf.append("ST_Force_3D (");
448
                                
449
                                sqlBuf.append(" ST_GeometryFromText( '"
450
                                + feat.getGeometry().toJTSGeometry().toText() + "', "
451
                                + DefaultJDBCDriver.removePrefix(dbLayerDef.getSRID_EPSG()) + ")");
452
                                
453
                                if (dbLayerDef.getDimension() == 3) sqlBuf.append(")");
454
                        }
455
                }
456
                sqlBuf.append(" WHERE ");
457
                sqlBuf.append(dbLayerDef.getFieldID() + " = " + feat.getID());
458
                sql = sqlBuf.toString();
459
                return sql;
460

    
461
        }
462

    
463
        /**
464
         * TODO: NECESITAMOS OTRO M?TODO PARA BORRAR CORRECTAMENTE.
465
         *         Esto provocar? errores, ya que getID que viene en un row no
466
         *         nos sirve dentro de un writer para modificar y/o borrar entidades
467
         *         Por ahora, cojo el ID del campo que me indica el dbLayerDef
468
         * @param dbLayerDef
469
         * @param row
470
         * @return
471
         */
472
        public String getSqlDeleteFeature(DBLayerDefinition dbLayerDef, IRow row) {
473
                // DELETE FROM weather WHERE city = 'Hayward';
474
                // TODO: NECESITAMOS OTRO M?TODO PARA BORRAR CORRECTAMENTE.
475
                // Esto provocar? errores, ya que getID que viene en un row no
476
                // nos sirve dentro de un writer para modificar y/o borrar entidades
477
                // Por ahora, cojo el ID del campo que me indica el dbLayerDev
478
                StringBuffer sqlBuf = new StringBuffer("DELETE FROM "
479
                                + dbLayerDef.getComposedTableName() + " WHERE ");
480
                String sql = null;
481
                int indexFieldId = dbLayerDef.getIdFieldID();
482
                sqlBuf.append("\""+dbLayerDef.getFieldID()+"\"" + " = " + row.getAttribute(indexFieldId));
483
                sql = sqlBuf.toString();
484

    
485
                return sql;
486
        }
487

    
488
        public String getEncoding() {
489
                return toEncode;
490
        }
491
        public void setEncoding(String toEncode){
492
                if (toEncode.compareToIgnoreCase("SQL_ASCII") == 0) {
493
                        this.toEncode = "ASCII";
494
                } else if (toEncode.compareToIgnoreCase("WIN1252") == 0) {
495
                        this.toEncode = "Latin1";
496
                } else if (toEncode.compareToIgnoreCase("UTF8") == 0) {
497
                        this.toEncode = "UTF-8";
498
                } else {
499
                        this.toEncode = toEncode;
500
                }
501
        }
502

    
503
        static String escapeFieldName(String name){
504
                if (!name.toLowerCase().equals(name)){
505
                        return "\""+name.trim()+"\"";
506
                }
507
                if (!name.matches("[a-z][\\d\\S\\w]*")){
508
                        return "\""+name.trim()+"\"";
509
                }
510
                if (name.indexOf(":")>0){
511
                        return "\""+name.trim()+"\"";
512
                }
513
                //si es una palabra reservada lo escapamos
514
                if (PostgresReservedWords.isReserved(name)){
515
                        return "\""+name.trim()+"\"";
516
                }
517
                return name;
518
        }
519
}