Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: netCDF read/write Driver
4 : * Purpose: GDAL bindings over netCDF library.
5 : * Author: Winor Chen <wchen329 at wisc.edu>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2019, Winor Chen <wchen329 at wisc.edu>
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 : #include "netcdfsg.h"
29 : #include "netcdfdataset.h"
30 : #include "ogr_core.h"
31 :
32 : namespace nccfdriver
33 : {
34 72 : OGRwkbGeometryType RawToOGR(geom_t type, int axis_count)
35 : {
36 72 : OGRwkbGeometryType ret = wkbNone;
37 :
38 72 : switch (type)
39 : {
40 0 : case NONE:
41 0 : break;
42 4 : case LINE:
43 6 : ret = axis_count == 2 ? wkbLineString
44 2 : : axis_count == 3 ? wkbLineString25D
45 : : wkbNone;
46 4 : break;
47 6 : case MULTILINE:
48 8 : ret = axis_count == 2 ? wkbMultiLineString
49 2 : : axis_count == 3 ? wkbMultiLineString25D
50 : : wkbNone;
51 6 : break;
52 8 : case POLYGON:
53 11 : ret = axis_count == 2 ? wkbPolygon
54 3 : : axis_count == 3 ? wkbPolygon25D
55 : : wkbNone;
56 8 : break;
57 33 : case MULTIPOLYGON:
58 39 : ret = axis_count == 2 ? wkbMultiPolygon
59 6 : : axis_count == 3 ? wkbMultiPolygon25D
60 : : wkbNone;
61 33 : break;
62 14 : case POINT:
63 25 : ret = axis_count == 2 ? wkbPoint
64 11 : : axis_count == 3 ? wkbPoint25D
65 : : wkbNone;
66 14 : break;
67 6 : case MULTIPOINT:
68 8 : ret = axis_count == 2 ? wkbMultiPoint
69 2 : : axis_count == 3 ? wkbMultiPoint25D
70 : : wkbNone;
71 6 : break;
72 1 : case UNSUPPORTED:
73 1 : break;
74 : }
75 :
76 72 : return ret;
77 : }
78 :
79 625 : geom_t OGRtoRaw(OGRwkbGeometryType type)
80 : {
81 625 : geom_t ret = NONE;
82 625 : auto eFlatType = wkbFlatten(type);
83 :
84 625 : if (eFlatType == wkbPoint)
85 : {
86 104 : ret = POINT;
87 : }
88 :
89 521 : else if (eFlatType == wkbLineString)
90 : {
91 12 : ret = LINE;
92 : }
93 :
94 509 : else if (eFlatType == wkbPolygon)
95 : {
96 42 : ret = POLYGON;
97 : }
98 :
99 467 : else if (eFlatType == wkbMultiPoint)
100 : {
101 19 : ret = MULTIPOINT;
102 : }
103 :
104 448 : else if (eFlatType == wkbMultiLineString)
105 : {
106 16 : ret = MULTILINE;
107 : }
108 :
109 432 : else if (eFlatType == wkbMultiPolygon)
110 : {
111 415 : ret = MULTIPOLYGON;
112 : }
113 :
114 : // if the feature type isn't NONE potentially give a warning about measures
115 625 : if (ret != NONE && wkbHasM(type))
116 : {
117 0 : CPLError(CE_Warning, CPLE_NotSupported,
118 : "A partially supported measured feature type was detected. X, "
119 : "Y, Z Geometry will be preserved but the measure axis and "
120 : "related information will be removed.");
121 : }
122 :
123 625 : return ret;
124 : }
125 :
126 42 : bool OGRHasZandSupported(OGRwkbGeometryType type)
127 : {
128 34 : return type == wkbPoint25D || type == wkbLineString25D ||
129 31 : type == wkbPolygon25D || type == wkbMultiPoint25D ||
130 76 : type == wkbMultiLineString25D || type == wkbMultiPolygon25D;
131 : }
132 :
133 : } // namespace nccfdriver
134 :
135 72 : bool netCDFDataset::DetectAndFillSGLayers(int ncid)
136 : {
137 : // Discover simple geometry variables
138 : int var_count;
139 72 : nc_inq_nvars(ncid, &var_count);
140 72 : std::set<int> vidList;
141 :
142 72 : nccfdriver::scanForGeometryContainers(ncid, vidList);
143 :
144 72 : if (!vidList.empty())
145 : {
146 148 : for (auto vid : vidList)
147 : {
148 : try
149 : {
150 78 : LoadSGVarIntoLayer(ncid, vid);
151 : }
152 :
153 14 : catch (nccfdriver::SG_Exception &e)
154 : {
155 7 : CPLError(CE_Warning, CPLE_AppDefined,
156 : "Translation of a simple geometry layer has been "
157 : "terminated prematurely due to an error.\n%s",
158 7 : e.get_err_msg());
159 : }
160 : }
161 : }
162 :
163 144 : return !vidList.empty();
164 : }
165 :
166 78 : CPLErr netCDFDataset::LoadSGVarIntoLayer(int ncid, int nc_basevarId)
167 : {
168 : std::shared_ptr<nccfdriver::SGeometry_Reader> sg(
169 150 : new nccfdriver::SGeometry_Reader(ncid, nc_basevarId));
170 72 : int cont_id = sg->getContainerId();
171 144 : nccfdriver::SGeometry_PropertyScanner pr(ncid, cont_id);
172 : OGRwkbGeometryType owgt =
173 72 : nccfdriver::RawToOGR(sg->getGeometryType(), sg->get_axisCount());
174 :
175 144 : std::string return_gm = "";
176 :
177 72 : if (sg->getGridMappingVarID() != nccfdriver::INVALID_VAR_ID)
178 96 : SetProjectionFromVar(ncid, nc_basevarId, true,
179 48 : sg->getGridMappingName().c_str(), &return_gm,
180 : sg.get(), /*paosRemovedMDItems=*/nullptr);
181 :
182 : // Geometry Type invalid, avoid further processing
183 72 : if (owgt == wkbNone)
184 : {
185 1 : throw nccfdriver::SG_Exception_BadFeature();
186 : }
187 :
188 : char baseName[NC_MAX_CHAR + 1];
189 71 : memset(baseName, 0, NC_MAX_CHAR + 1);
190 71 : nc_inq_varname(ncid, nc_basevarId, baseName);
191 :
192 71 : OGRSpatialReference *poSRS = nullptr;
193 71 : if (return_gm != "")
194 : {
195 48 : poSRS = new OGRSpatialReference();
196 48 : if (poSRS->importFromWkt(return_gm.c_str()) != OGRERR_NONE)
197 : {
198 0 : delete poSRS;
199 0 : throw nccfdriver::SG_Exception_General_Malformed("SRS settings");
200 : }
201 :
202 48 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
203 : }
204 :
205 : std::shared_ptr<netCDFLayer> poL(
206 142 : new netCDFLayer(this, ncid, baseName, owgt, poSRS));
207 :
208 71 : if (poSRS != nullptr)
209 : {
210 48 : poSRS->Release();
211 : }
212 :
213 71 : poL->EnableSGBypass();
214 71 : OGRFeatureDefn *defn = poL->GetLayerDefn();
215 71 : defn->SetGeomType(owgt);
216 :
217 : // Add properties
218 71 : std::vector<int> props = pr.ids();
219 188 : for (size_t itr = 0; itr < props.size(); itr++)
220 : {
221 117 : poL->AddField(props[itr]);
222 : }
223 :
224 : // Set simple geometry object
225 71 : poL->SetSGeometryRepresentation(sg);
226 :
227 : // Create layer
228 71 : papoLayers.push_back(poL);
229 :
230 142 : return CE_None;
231 : }
232 :
233 : /* Creates and fills any needed variables that haven't already been created
234 : */
235 961 : bool netCDFDataset::SGCommitPendingTransaction()
236 : {
237 : try
238 : {
239 961 : if (bSGSupport)
240 : {
241 : // Go through all the layers and resize dimensions accordingly
242 223 : for (size_t layerInd = 0; layerInd < papoLayers.size(); layerInd++)
243 : {
244 : auto poLayer =
245 114 : dynamic_cast<netCDFLayer *>(papoLayers[layerInd].get());
246 114 : if (!poLayer)
247 0 : continue;
248 : nccfdriver::ncLayer_SG_Metadata &layerMD =
249 114 : poLayer->getLayerSGMetadata();
250 114 : nccfdriver::geom_t wType = layerMD.getWritableType();
251 :
252 : // Resize node coordinates
253 114 : int ncoord_did = layerMD.get_node_coord_dimID();
254 114 : if (ncoord_did != nccfdriver::INVALID_DIM_ID)
255 : {
256 42 : vcdf.nc_resize_vdim(
257 : ncoord_did, layerMD.get_next_write_pos_node_coord());
258 : }
259 :
260 : // Resize node count (for all except POINT)
261 114 : if (wType != nccfdriver::POINT)
262 : {
263 89 : int ncount_did = layerMD.get_node_count_dimID();
264 89 : if (ncount_did != nccfdriver::INVALID_DIM_ID)
265 : {
266 32 : vcdf.nc_resize_vdim(
267 : ncount_did,
268 : layerMD.get_next_write_pos_node_count());
269 : }
270 : }
271 :
272 : // Resize part node count (for MULTILINE, POLYGON, MULTIPOLYGON)
273 114 : if (wType == nccfdriver::MULTILINE ||
274 91 : wType == nccfdriver::POLYGON ||
275 : wType == nccfdriver::MULTIPOLYGON)
276 : {
277 74 : int pnc_did = layerMD.get_pnc_dimID();
278 74 : if (pnc_did != nccfdriver::INVALID_DIM_ID)
279 : {
280 27 : vcdf.nc_resize_vdim(pnc_did,
281 : layerMD.get_next_write_pos_pnc());
282 : }
283 : }
284 :
285 114 : nccfdriver::geom_t geometry_type = layerMD.getWritableType();
286 :
287 : /* Delete interior ring stuff if not detected
288 : */
289 :
290 221 : if (!layerMD.getInteriorRingDetected() &&
291 61 : (geometry_type == nccfdriver::MULTIPOLYGON ||
292 221 : geometry_type == nccfdriver::POLYGON) &&
293 58 : layerMD.get_containerRealID() != nccfdriver::INVALID_VAR_ID)
294 : {
295 17 : SetDefineMode(true);
296 :
297 : int err_code =
298 17 : nc_del_att(cdfid, layerMD.get_containerRealID(),
299 : CF_SG_INTERIOR_RING);
300 17 : NCDF_ERR(err_code);
301 17 : if (err_code != NC_NOERR)
302 : {
303 0 : std::string frmt = std::string("attribute: ") +
304 0 : std::string(CF_SG_INTERIOR_RING);
305 : throw nccfdriver::SGWriter_Exception_NCDelFailure(
306 0 : layerMD.get_containerName().c_str(), frmt.c_str());
307 : }
308 :
309 : // Invalidate variable writes as well - Interior Ring
310 17 : vcdf.nc_del_vvar(layerMD.get_intring_varID());
311 :
312 17 : if (geometry_type == nccfdriver::POLYGON)
313 : {
314 : err_code =
315 4 : nc_del_att(cdfid, layerMD.get_containerRealID(),
316 : CF_SG_PART_NODE_COUNT);
317 4 : NCDF_ERR(err_code);
318 4 : if (err_code != NC_NOERR)
319 : {
320 : std::string frmt =
321 0 : std::string("attribute: ") +
322 0 : std::string(CF_SG_PART_NODE_COUNT);
323 : throw nccfdriver::SGWriter_Exception_NCDelFailure(
324 0 : layerMD.get_containerName().c_str(),
325 0 : frmt.c_str());
326 : }
327 :
328 : // Invalidate variable writes as well - Part Node Count
329 4 : vcdf.nc_del_vvar(layerMD.get_pnc_varID());
330 :
331 : // Invalidate dimension as well - Part Node Count
332 4 : vcdf.nc_del_vdim(layerMD.get_pnc_dimID());
333 : }
334 :
335 17 : SetDefineMode(false);
336 : }
337 : }
338 :
339 109 : vcdf.nc_vmap();
340 109 : this->FieldScribe.commit_transaction();
341 109 : this->GeometryScribe.commit_transaction();
342 : }
343 : }
344 :
345 0 : catch (nccfdriver::SG_Exception &sge)
346 : {
347 0 : CPLError(CE_Fatal, CPLE_FileIO,
348 : "An error occurred while writing the target netCDF File. "
349 : "Translation will be terminated.\n%s",
350 0 : sge.get_err_msg());
351 0 : return false;
352 : }
353 961 : return true;
354 : }
355 :
356 180 : void netCDFDataset::SGLogPendingTransaction()
357 : {
358 180 : GeometryScribe.log_transaction();
359 180 : FieldScribe.log_transaction();
360 180 : }
361 :
362 : /* Takes an index and using the layer geometry builds the equivalent
363 : * OGRFeature.
364 : */
365 492 : OGRFeature *netCDFLayer::buildSGeometryFeature(size_t featureInd)
366 : {
367 : OGRGeometry *geometry;
368 :
369 492 : switch (m_simpleGeometryReader->getGeometryType())
370 : {
371 39 : case nccfdriver::POINT:
372 39 : geometry = new OGRPoint;
373 39 : break;
374 16 : case nccfdriver::LINE:
375 16 : geometry = new OGRLineString;
376 16 : break;
377 13 : case nccfdriver::POLYGON:
378 13 : geometry = new OGRPolygon;
379 13 : break;
380 25 : case nccfdriver::MULTIPOINT:
381 25 : geometry = new OGRMultiPoint;
382 25 : break;
383 18 : case nccfdriver::MULTILINE:
384 18 : geometry = new OGRMultiLineString;
385 18 : break;
386 381 : case nccfdriver::MULTIPOLYGON:
387 381 : geometry = new OGRMultiPolygon;
388 381 : break;
389 0 : default:
390 0 : throw nccfdriver::SG_Exception_BadFeature();
391 : break;
392 : }
393 :
394 492 : const auto wkb = m_simpleGeometryReader->serializeToWKB(featureInd);
395 492 : geometry->importFromWkb(wkb.data(), wkb.size(), wkbVariantIso);
396 492 : geometry->assignSpatialReference(this->GetSpatialRef());
397 :
398 492 : OGRFeatureDefn *defn = this->GetLayerDefn();
399 492 : OGRFeature *feat = new OGRFeature(defn);
400 492 : feat->SetGeometryDirectly(geometry);
401 :
402 492 : int dimId = m_simpleGeometryReader->getInstDim();
403 :
404 492 : this->FillFeatureFromVar(feat, dimId, featureInd);
405 :
406 492 : feat->SetFID(featureInd);
407 984 : return feat;
408 : }
409 :
410 1922 : std::string netCDFDataset::generateLogName()
411 : {
412 1922 : return std::string(CPLGenerateTempFilename(nullptr));
413 : }
|