1 /*
2  * Copyright 2013 Luciad (http://www.luciad.com)
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <stdio.h>
17 #include <string.h>
18 #include "fp.h"
19 #include "spl_geom.h"
20 #include "sqlite.h"
21 
22 #define SPB_BIG_ENDIAN 0x00
23 #define SPB_LITTLE_ENDIAN 0x01
24 
25 #define CHECK_ENV_COMP(spb, comp, error) \
26     if (spb->envelope.has_env_##comp) { \
27       if ((spb->empty && (!fp_isnan(spb->envelope.min_##comp) || !fp_isnan(spb->envelope.max_##comp))) || spb->envelope.min_##comp > spb->envelope.max_##comp) {\
28           if (error) error_append(error, "SPB envelope min" #comp " > max" #comp ": [min: %g, max: %g]", spb->envelope.min_##comp, spb->envelope.max_##comp);\
29           return SQLITE_IOERR;\
30       }\
31     }
32 #define CHECK_ENV(spb, error) CHECK_ENV_COMP(spb, x, error) CHECK_ENV_COMP(spb, y, error) CHECK_ENV_COMP(spb, z, error) CHECK_ENV_COMP(spb, m, error)
33 
spb_read_header(binstream_t * stream,geom_blob_header_t * spb,error_t * error)34 int spb_read_header(binstream_t *stream, geom_blob_header_t *spb, error_t *error) {
35   uint8_t start;
36   if (binstream_read_u8(stream, &start) != SQLITE_OK) {
37     return SQLITE_IOERR;
38   }
39 
40   if (start != 0x00) {
41     if (error) {
42       error_append(error, "Incorrect SPB START value [expected: 00, actual:%x]", start);
43     }
44     return SQLITE_IOERR;
45   }
46 
47   uint8_t endian;
48   if (binstream_read_u8(stream, &endian)) {
49     return SQLITE_IOERR;
50   }
51 
52   if (endian != SPB_BIG_ENDIAN && endian != SPB_LITTLE_ENDIAN) {
53     if (error) {
54       error_append(error, "Incorrect SPB ENDIAN value [expected: 00 or 01, actual:%x]", endian);
55     }
56     return SQLITE_IOERR;
57   }
58 
59   binstream_set_endianness(stream, endian == SPB_BIG_ENDIAN ? BIG : LITTLE);
60   if (binstream_read_i32(stream, &spb->srid) != SQLITE_OK) {
61     return SQLITE_IOERR;
62   }
63 
64   spb->envelope.has_env_x = 1;
65   spb->envelope.has_env_y = 1;
66   spb->envelope.has_env_z = 0;
67   spb->envelope.has_env_m = 0;
68   if (binstream_read_double(stream, &spb->envelope.min_x)) {
69     return SQLITE_IOERR;
70   }
71   if (binstream_read_double(stream, &spb->envelope.min_y)) {
72     return SQLITE_IOERR;
73   }
74   if (binstream_read_double(stream, &spb->envelope.max_x)) {
75     return SQLITE_IOERR;
76   }
77   if (binstream_read_double(stream, &spb->envelope.max_y)) {
78     return SQLITE_IOERR;
79   }
80 
81   spb->empty = fp_isnan(spb->envelope.min_x) && fp_isnan(spb->envelope.max_x) && fp_isnan(spb->envelope.min_y) && fp_isnan(spb->envelope.max_y);
82 
83   CHECK_ENV(spb, error)
84 
85   return SQLITE_OK;
86 }
87 
spb_write_header(binstream_t * stream,geom_blob_header_t * spb,error_t * error)88 int spb_write_header(binstream_t *stream, geom_blob_header_t *spb, error_t *error) {
89   CHECK_ENV(spb, error)
90 
91   if (binstream_write_u8(stream, 0x00)) {
92     return SQLITE_IOERR;
93   }
94 
95   uint8_t endian = binstream_get_endianness(stream) == LITTLE ? SPB_LITTLE_ENDIAN : SPB_BIG_ENDIAN;
96   if (binstream_write_u8(stream, endian)) {
97     return SQLITE_IOERR;
98   }
99 
100   if (binstream_write_i32(stream, spb->srid)) {
101     return SQLITE_IOERR;
102   }
103 
104   if (binstream_write_double(stream, spb->envelope.min_x)) {
105     return SQLITE_IOERR;
106   }
107   if (binstream_write_double(stream, spb->envelope.min_y)) {
108     return SQLITE_IOERR;
109   }
110   if (binstream_write_double(stream, spb->envelope.max_x)) {
111     return SQLITE_IOERR;
112   }
113   if (binstream_write_double(stream, spb->envelope.max_y)) {
114     return SQLITE_IOERR;
115   }
116 
117   return SQLITE_OK;
118 }
119 
spb_begin_geometry(const geom_consumer_t * consumer,const geom_header_t * header,error_t * error)120 static int spb_begin_geometry(const geom_consumer_t *consumer, const geom_header_t *header, error_t *error) {
121   int result = SQLITE_OK;
122 
123   spb_writer_t *writer = (spb_writer_t *) consumer;
124   wkb_writer_t *wkb = &writer->wkb_writer;
125 
126   if (wkb->offset < 0) {
127     result = binstream_relseek(&wkb->stream, 38);
128     if (result != SQLITE_OK) {
129       goto exit;
130     }
131   }
132 
133   geom_consumer_t *wkb_consumer = wkb_writer_geom_consumer(wkb);
134   result = wkb_consumer->begin_geometry(wkb_consumer, header, error);
135 exit:
136   return result;
137 }
138 
spb_coordinates(const geom_consumer_t * consumer,const geom_header_t * header,size_t point_count,const double * coords,int skip_coords,error_t * error)139 static int spb_coordinates(const geom_consumer_t *consumer, const geom_header_t *header, size_t point_count, const double *coords, int skip_coords, error_t *error) {
140   int result = SQLITE_OK;
141 
142   if (point_count <= 0) {
143     goto exit;
144   }
145 
146   spb_writer_t *writer = (spb_writer_t *) consumer;
147   wkb_writer_t *wkb = &writer->wkb_writer;
148   geom_consumer_t *wkb_consumer = wkb_writer_geom_consumer(wkb);
149   result = wkb_consumer->coordinates(wkb_consumer, header, point_count, coords, skip_coords, error);
150   if (result != SQLITE_OK) {
151     goto exit;
152   }
153 
154   if (header->geom_type == GEOM_POINT) {
155     int allnan = 1;
156     for (uint32_t i = 0; i < header->coord_size; i++) {
157       allnan &= fp_isnan(coords[i]);
158     }
159     if (allnan) {
160       goto exit;
161     }
162   }
163 
164   geom_blob_header_t *spb = &writer->header;
165   spb->empty = 0;
166   geom_envelope_t *envelope = &spb->envelope;
167   geom_envelope_fill(envelope, header, point_count, coords);
168 
169 exit:
170   return result;
171 }
172 
spb_end_geometry(const geom_consumer_t * consumer,const geom_header_t * header,error_t * error)173 static int spb_end_geometry(const geom_consumer_t *consumer, const geom_header_t *header, error_t *error) {
174   spb_writer_t *writer = (spb_writer_t *) consumer;
175   wkb_writer_t *wkb = &writer->wkb_writer;
176 
177   geom_consumer_t *wkb_consumer = wkb_writer_geom_consumer(wkb);
178   return wkb_consumer->end_geometry(wkb_consumer, header, error);
179 }
180 
spb_end(const geom_consumer_t * consumer,error_t * error)181 static int spb_end(const geom_consumer_t *consumer, error_t *error) {
182   int result = SQLITE_OK;
183 
184   spb_writer_t *writer = (spb_writer_t *) consumer;
185   wkb_writer_t *wkb = &writer->wkb_writer;
186   binstream_t *stream = &wkb->stream;
187 
188   size_t pos = binstream_position(stream);
189   result = binstream_seek(stream, 0);
190   if (result != SQLITE_OK) {
191     goto exit;
192   }
193 
194   geom_envelope_t *envelope = &writer->header.envelope;
195   if (geom_envelope_finalize(envelope) == EMPTY_GEOM) {
196     writer->header.empty = 1;
197   }
198 
199   result = spb_write_header(stream, &writer->header, NULL);
200   if (result != SQLITE_OK) {
201     goto exit;
202   }
203 
204   result = binstream_seek(stream, pos);
205   if (result != SQLITE_OK) {
206     goto exit;
207   }
208 
209   geom_consumer_t *wkb_consumer = wkb_writer_geom_consumer(wkb);
210   result = wkb_consumer->end(wkb_consumer, error);
211 
212 exit:
213   return result;
214 }
215 
spb_writer_init(geom_blob_writer_t * writer,int32_t srid)216 int spb_writer_init(geom_blob_writer_t *writer, int32_t srid) {
217   geom_consumer_init(&writer->geom_consumer, NULL, spb_end, spb_begin_geometry, spb_end_geometry, spb_coordinates);
218   geom_envelope_init(&writer->header.envelope);
219   writer->header.envelope.has_env_x = 1;
220   writer->header.envelope.has_env_y = 1;
221   writer->header.srid = srid;
222   writer->header.empty = 1;
223   return wkb_writer_init(&writer->wkb_writer, WKB_SPATIALITE);
224 }
225 
spb_writer_destroy(geom_blob_writer_t * writer,int free_data)226 void spb_writer_destroy(geom_blob_writer_t *writer, int free_data) {
227   wkb_writer_destroy(&writer->wkb_writer, free_data);
228 }
229