Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements OGRDGNLayer class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2000, Frank Warmerdam (warmerdam@pobox.com)
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 :
29 : #include "ogr_dgn.h"
30 : #include "cpl_conv.h"
31 : #include "ogr_featurestyle.h"
32 : #include "ogr_api.h"
33 :
34 : #include <algorithm>
35 : #include <cmath>
36 : #include <list>
37 :
38 : /************************************************************************/
39 : /* OGRDGNLayer() */
40 : /************************************************************************/
41 :
42 73 : OGRDGNLayer::OGRDGNLayer(GDALDataset *poDS, const char *pszName,
43 73 : DGNHandle hDGNIn, int bUpdateIn)
44 73 : : m_poDS(poDS), poFeatureDefn(new OGRFeatureDefn(pszName)), iNextShapeId(0),
45 73 : hDGN(hDGNIn), bUpdate(bUpdateIn)
46 : {
47 :
48 : /* -------------------------------------------------------------------- */
49 : /* Work out what link format we are using. */
50 : /* -------------------------------------------------------------------- */
51 : OGRFieldType eLinkFieldType;
52 :
53 73 : pszLinkFormat =
54 73 : const_cast<char *>(CPLGetConfigOption("DGN_LINK_FORMAT", "FIRST"));
55 :
56 73 : if (EQUAL(pszLinkFormat, "FIRST"))
57 73 : eLinkFieldType = OFTInteger;
58 0 : else if (EQUAL(pszLinkFormat, "LIST"))
59 0 : eLinkFieldType = OFTIntegerList;
60 0 : else if (EQUAL(pszLinkFormat, "STRING"))
61 0 : eLinkFieldType = OFTString;
62 : else
63 : {
64 0 : CPLError(CE_Warning, CPLE_AppDefined,
65 : "DGN_LINK_FORMAT=%s, but only FIRST, LIST or STRING "
66 : "supported.",
67 : pszLinkFormat);
68 0 : pszLinkFormat = const_cast<char *>("FIRST");
69 0 : eLinkFieldType = OFTInteger;
70 : }
71 73 : pszLinkFormat = CPLStrdup(pszLinkFormat);
72 :
73 : /* -------------------------------------------------------------------- */
74 : /* Create the feature definition. */
75 : /* -------------------------------------------------------------------- */
76 73 : SetDescription(poFeatureDefn->GetName());
77 73 : poFeatureDefn->Reference();
78 :
79 73 : OGRFieldDefn oField("", OFTInteger);
80 :
81 : /* -------------------------------------------------------------------- */
82 : /* Element type */
83 : /* -------------------------------------------------------------------- */
84 73 : oField.SetName("Type");
85 73 : oField.SetType(OFTInteger);
86 73 : oField.SetWidth(2);
87 73 : oField.SetPrecision(0);
88 73 : poFeatureDefn->AddFieldDefn(&oField);
89 :
90 : /* -------------------------------------------------------------------- */
91 : /* Level number. */
92 : /* -------------------------------------------------------------------- */
93 73 : oField.SetName("Level");
94 73 : oField.SetType(OFTInteger);
95 73 : oField.SetWidth(2);
96 73 : oField.SetPrecision(0);
97 73 : poFeatureDefn->AddFieldDefn(&oField);
98 :
99 : /* -------------------------------------------------------------------- */
100 : /* graphic group */
101 : /* -------------------------------------------------------------------- */
102 73 : oField.SetName("GraphicGroup");
103 73 : oField.SetType(OFTInteger);
104 73 : oField.SetWidth(4);
105 73 : oField.SetPrecision(0);
106 73 : poFeatureDefn->AddFieldDefn(&oField);
107 :
108 : /* -------------------------------------------------------------------- */
109 : /* ColorIndex */
110 : /* -------------------------------------------------------------------- */
111 73 : oField.SetName("ColorIndex");
112 73 : oField.SetType(OFTInteger);
113 73 : oField.SetWidth(3);
114 73 : oField.SetPrecision(0);
115 73 : poFeatureDefn->AddFieldDefn(&oField);
116 :
117 : /* -------------------------------------------------------------------- */
118 : /* Weight */
119 : /* -------------------------------------------------------------------- */
120 73 : oField.SetName("Weight");
121 73 : oField.SetType(OFTInteger);
122 73 : oField.SetWidth(2);
123 73 : oField.SetPrecision(0);
124 73 : poFeatureDefn->AddFieldDefn(&oField);
125 :
126 : /* -------------------------------------------------------------------- */
127 : /* Style */
128 : /* -------------------------------------------------------------------- */
129 73 : oField.SetName("Style");
130 73 : oField.SetType(OFTInteger);
131 73 : oField.SetWidth(1);
132 73 : oField.SetPrecision(0);
133 73 : poFeatureDefn->AddFieldDefn(&oField);
134 :
135 : /* -------------------------------------------------------------------- */
136 : /* EntityNum */
137 : /* -------------------------------------------------------------------- */
138 73 : oField.SetName("EntityNum");
139 73 : oField.SetType(eLinkFieldType);
140 73 : oField.SetWidth(0);
141 73 : oField.SetPrecision(0);
142 73 : poFeatureDefn->AddFieldDefn(&oField);
143 :
144 : /* -------------------------------------------------------------------- */
145 : /* MSLink */
146 : /* -------------------------------------------------------------------- */
147 73 : oField.SetName("MSLink");
148 73 : oField.SetType(eLinkFieldType);
149 73 : oField.SetWidth(0);
150 73 : oField.SetPrecision(0);
151 73 : poFeatureDefn->AddFieldDefn(&oField);
152 :
153 : /* -------------------------------------------------------------------- */
154 : /* Text */
155 : /* -------------------------------------------------------------------- */
156 73 : oField.SetName("Text");
157 73 : oField.SetType(OFTString);
158 73 : oField.SetWidth(0);
159 73 : oField.SetPrecision(0);
160 73 : poFeatureDefn->AddFieldDefn(&oField);
161 :
162 : /* -------------------------------------------------------------------- */
163 : /* ULink */
164 : /* -------------------------------------------------------------------- */
165 73 : oField.SetName("ULink");
166 73 : oField.SetType(OFTString);
167 73 : oField.SetSubType(OFSTJSON);
168 73 : oField.SetWidth(0);
169 73 : oField.SetPrecision(0);
170 73 : poFeatureDefn->AddFieldDefn(&oField);
171 :
172 : /* -------------------------------------------------------------------- */
173 : /* Create template feature for evaluating simple expressions. */
174 : /* -------------------------------------------------------------------- */
175 73 : poEvalFeature = new OGRFeature(poFeatureDefn);
176 :
177 : /* TODO: I am intending to keep track of simple attribute queries (ones
178 : using only FID, Type and Level and short circuiting their operation
179 : based on the index. However, there are some complexities with
180 : complex elements, and spatial queries that have caused me to put it
181 : off for now.
182 : */
183 73 : }
184 :
185 : /************************************************************************/
186 : /* ~OGRDGNLayer() */
187 : /************************************************************************/
188 :
189 146 : OGRDGNLayer::~OGRDGNLayer()
190 :
191 : {
192 73 : if (m_nFeaturesRead > 0)
193 : {
194 48 : CPLDebug("Mem", "%d features read on layer '%s'.",
195 24 : static_cast<int>(m_nFeaturesRead), poFeatureDefn->GetName());
196 : }
197 :
198 73 : delete poEvalFeature;
199 :
200 73 : poFeatureDefn->Release();
201 :
202 73 : CPLFree(pszLinkFormat);
203 146 : }
204 :
205 : /************************************************************************/
206 : /* SetSpatialFilter() */
207 : /************************************************************************/
208 :
209 4 : void OGRDGNLayer::SetSpatialFilter(OGRGeometry *poGeomIn)
210 :
211 : {
212 4 : if (!InstallFilter(poGeomIn))
213 2 : return;
214 :
215 2 : if (m_poFilterGeom != nullptr)
216 : {
217 1 : DGNSetSpatialFilter(hDGN, m_sFilterEnvelope.MinX,
218 : m_sFilterEnvelope.MinY, m_sFilterEnvelope.MaxX,
219 : m_sFilterEnvelope.MaxY);
220 : }
221 : else
222 : {
223 1 : DGNSetSpatialFilter(hDGN, 0.0, 0.0, 0.0, 0.0);
224 : }
225 :
226 2 : ResetReading();
227 : }
228 :
229 : /************************************************************************/
230 : /* ResetReading() */
231 : /************************************************************************/
232 :
233 24 : void OGRDGNLayer::ResetReading()
234 :
235 : {
236 24 : iNextShapeId = 0;
237 24 : DGNRewind(hDGN);
238 24 : }
239 :
240 : /************************************************************************/
241 : /* GetFeature() */
242 : /************************************************************************/
243 :
244 0 : OGRFeature *OGRDGNLayer::GetFeature(GIntBig nFeatureId)
245 :
246 : {
247 0 : if (nFeatureId > INT_MAX || !DGNGotoElement(hDGN, (int)nFeatureId))
248 0 : return nullptr;
249 :
250 : // We should likely clear the spatial search region as it affects
251 : // DGNReadElement(), but I will defer that for now.
252 :
253 0 : DGNElemCore *psElement = DGNReadElement(hDGN);
254 0 : OGRFeature *poFeature = ElementToFeature(psElement, 0);
255 0 : DGNFreeElement(hDGN, psElement);
256 :
257 0 : if (poFeature == nullptr)
258 0 : return nullptr;
259 :
260 0 : if (poFeature->GetFID() != nFeatureId)
261 : {
262 0 : delete poFeature;
263 0 : return nullptr;
264 : }
265 :
266 0 : return poFeature;
267 : }
268 :
269 : /************************************************************************/
270 : /* ConsiderBrush() */
271 : /* */
272 : /* Method to set the style for a polygon, including a brush if */
273 : /* appropriate. */
274 : /************************************************************************/
275 :
276 10 : void OGRDGNLayer::ConsiderBrush(DGNElemCore *psElement, const char *pszPen,
277 : OGRFeature *poFeature)
278 :
279 : {
280 10 : int nFillColor = 0;
281 10 : int gv_red = 0;
282 10 : int gv_green = 0;
283 10 : int gv_blue = 0;
284 :
285 14 : if (DGNGetShapeFillInfo(hDGN, psElement, &nFillColor) &&
286 4 : DGNLookupColor(hDGN, nFillColor, &gv_red, &gv_green, &gv_blue))
287 : {
288 8 : CPLString osFullStyle;
289 4 : osFullStyle.Printf("BRUSH(fc:#%02x%02x%02x,id:\"ogr-brush-0\")", gv_red,
290 4 : gv_green, gv_blue);
291 :
292 4 : if (nFillColor != psElement->color)
293 : {
294 0 : osFullStyle += ';';
295 0 : osFullStyle += pszPen;
296 : }
297 4 : poFeature->SetStyleString(osFullStyle.c_str());
298 : }
299 : else
300 6 : poFeature->SetStyleString(pszPen);
301 10 : }
302 :
303 : /************************************************************************/
304 : /* ElementToFeature() */
305 : /************************************************************************/
306 :
307 258 : OGRFeature *OGRDGNLayer::ElementToFeature(DGNElemCore *psElement, int nRecLevel)
308 :
309 : {
310 258 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
311 :
312 258 : poFeature->SetFID(psElement->element_id);
313 258 : poFeature->SetField("Type", psElement->type);
314 258 : poFeature->SetField("Level", psElement->level);
315 258 : poFeature->SetField("GraphicGroup", psElement->graphic_group);
316 258 : poFeature->SetField("ColorIndex", psElement->color);
317 258 : poFeature->SetField("Weight", psElement->weight);
318 258 : poFeature->SetField("Style", psElement->style);
319 :
320 258 : m_nFeaturesRead++;
321 :
322 : /* -------------------------------------------------------------------- */
323 : /* Collect linkage information */
324 : /* -------------------------------------------------------------------- */
325 258 : const int MAX_LINK = 100;
326 :
327 : int anEntityNum[MAX_LINK];
328 258 : anEntityNum[0] = 0;
329 :
330 : int anMSLink[MAX_LINK];
331 258 : anMSLink[0] = 0;
332 :
333 258 : CPLJSONObject uLinkData;
334 :
335 258 : int iLink = 0;
336 258 : int nLinkCount = 0;
337 258 : int uLinkCount = 0;
338 :
339 258 : int nLinkType = 0;
340 258 : int nLinkSize = 0;
341 :
342 : // coverity[tained_data]
343 : unsigned char *pabyData =
344 258 : DGNGetLinkage(hDGN, psElement, iLink, &nLinkType, anEntityNum + iLink,
345 258 : anMSLink + iLink, &nLinkSize);
346 :
347 282 : while (pabyData)
348 : {
349 : CPLJSONArray previousValues =
350 24 : uLinkData.GetArray(std::to_string(nLinkType));
351 24 : if (!previousValues.IsValid())
352 : {
353 24 : uLinkData.Add(std::to_string(nLinkType), CPLJSONArray());
354 24 : previousValues = uLinkData.GetArray(std::to_string(nLinkType));
355 : }
356 24 : CPLJSONArray rawWords;
357 212 : for (int i = 0; i < nLinkSize - 1; i += 2)
358 : {
359 188 : rawWords.Add(
360 188 : CPLSPrintf("0x%02x%02x", pabyData[i + 1], pabyData[i]));
361 : }
362 24 : CPLJSONObject theNewObject = CPLJSONObject();
363 24 : theNewObject.Add("size", nLinkSize);
364 24 : previousValues.Add(theNewObject);
365 24 : switch (nLinkType)
366 : {
367 0 : case 24721: // OdDgDBLinkage::kOracle
368 : {
369 0 : theNewObject.Add("raw", rawWords);
370 0 : theNewObject.Add("type", "Oracle");
371 : }
372 0 : break;
373 0 : case 32047: // OdDgDBLinkage::kODBC
374 : {
375 0 : theNewObject.Add("raw", rawWords);
376 0 : theNewObject.Add("type", "ODBC");
377 : }
378 0 : break;
379 0 : case 6549: // 0x1995 Application ID by IPCC/Portugal
380 : {
381 0 : theNewObject.Add("domain", CPLSPrintf("0x%02x", pabyData[5]));
382 0 : theNewObject.Add("subdomain",
383 0 : CPLSPrintf("0x%02x", pabyData[4]));
384 0 : theNewObject.Add("family", CPLSPrintf("0x%02x", pabyData[7]));
385 0 : theNewObject.Add("object", CPLSPrintf("0x%02x", pabyData[6]));
386 0 : theNewObject.Add("key", CPLSPrintf("%02x%02x%02x%02x",
387 0 : pabyData[5], pabyData[4],
388 0 : pabyData[7], pabyData[6]));
389 0 : theNewObject.Add("type", "IPCC/Portugal");
390 : }
391 0 : break;
392 24 : default:
393 : {
394 24 : theNewObject.Add("raw", rawWords);
395 24 : theNewObject.Add("type", "unknown");
396 : }
397 24 : break;
398 : }
399 :
400 24 : uLinkCount++;
401 24 : iLink++;
402 :
403 24 : if (anEntityNum[nLinkCount] != 0 || anMSLink[nLinkCount] != 0)
404 : {
405 0 : nLinkCount++;
406 0 : if (nLinkCount == MAX_LINK)
407 : {
408 0 : break;
409 : }
410 : }
411 :
412 24 : anEntityNum[nLinkCount] = 0;
413 24 : anMSLink[nLinkCount] = 0;
414 :
415 : // coverity[tained_data]
416 24 : pabyData = DGNGetLinkage(hDGN, psElement, iLink, &nLinkType,
417 24 : anEntityNum + nLinkCount,
418 24 : anMSLink + nLinkCount, &nLinkSize);
419 : }
420 :
421 : /* -------------------------------------------------------------------- */
422 : /* Apply attribute linkage to feature. */
423 : /* -------------------------------------------------------------------- */
424 258 : if (uLinkCount > 0)
425 : {
426 23 : poFeature->SetField("ULink", uLinkData.ToString().c_str());
427 : }
428 258 : if (nLinkCount > 0)
429 : {
430 0 : if (EQUAL(pszLinkFormat, "FIRST"))
431 : {
432 0 : poFeature->SetField("EntityNum", anEntityNum[0]);
433 0 : poFeature->SetField("MSLink", anMSLink[0]);
434 : }
435 0 : else if (EQUAL(pszLinkFormat, "LIST"))
436 : {
437 0 : poFeature->SetField("EntityNum", nLinkCount, anEntityNum);
438 0 : poFeature->SetField("MSLink", nLinkCount, anMSLink);
439 : }
440 0 : else if (EQUAL(pszLinkFormat, "STRING"))
441 : {
442 : char szEntityList[MAX_LINK * 9];
443 : char szMSLinkList[MAX_LINK * 9];
444 0 : int nEntityLen = 0;
445 0 : int nMSLinkLen = 0;
446 :
447 0 : for (iLink = 0; iLink < nLinkCount; iLink++)
448 : {
449 0 : if (iLink != 0)
450 : {
451 0 : szEntityList[nEntityLen++] = ',';
452 0 : szMSLinkList[nMSLinkLen++] = ',';
453 : }
454 :
455 0 : snprintf(szEntityList + nEntityLen,
456 0 : sizeof(szEntityList) - nEntityLen, "%d",
457 : anEntityNum[iLink]);
458 0 : snprintf(szMSLinkList + nMSLinkLen,
459 0 : sizeof(szMSLinkList) - nMSLinkLen, "%d",
460 : anMSLink[iLink]);
461 :
462 0 : nEntityLen +=
463 0 : static_cast<int>(strlen(szEntityList + nEntityLen));
464 0 : nMSLinkLen +=
465 0 : static_cast<int>(strlen(szMSLinkList + nMSLinkLen));
466 : }
467 :
468 0 : poFeature->SetField("EntityNum", szEntityList);
469 0 : poFeature->SetField("MSLink", szMSLinkList);
470 : }
471 : }
472 :
473 : /* -------------------------------------------------------------------- */
474 : /* Lookup color. */
475 : /* -------------------------------------------------------------------- */
476 258 : int gv_red = 0;
477 258 : int gv_green = 0;
478 258 : int gv_blue = 0;
479 :
480 258 : char szFSColor[128] = {};
481 258 : szFSColor[0] = '\0';
482 258 : if (DGNLookupColor(hDGN, psElement->color, &gv_red, &gv_green, &gv_blue))
483 : {
484 : char gv_color[128];
485 258 : CPLsnprintf(gv_color, sizeof(gv_color), "%f %f %f 1.0", gv_red / 255.0,
486 : gv_green / 255.0, gv_blue / 255.0);
487 :
488 258 : snprintf(szFSColor, sizeof(szFSColor), "c:#%02x%02x%02x", gv_red,
489 : gv_green, gv_blue);
490 : }
491 :
492 : /* -------------------------------------------------------------------- */
493 : /* Generate corresponding PEN style. */
494 : /* -------------------------------------------------------------------- */
495 : char szPen[256];
496 258 : szPen[0] = '\0';
497 :
498 258 : if (psElement->style == DGNS_SOLID)
499 234 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-0\"");
500 24 : else if (psElement->style == DGNS_DOTTED)
501 24 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-5\"");
502 0 : else if (psElement->style == DGNS_MEDIUM_DASH)
503 0 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-2\"");
504 0 : else if (psElement->style == DGNS_LONG_DASH)
505 0 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-4\"");
506 0 : else if (psElement->style == DGNS_DOT_DASH)
507 0 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-6\"");
508 0 : else if (psElement->style == DGNS_SHORT_DASH)
509 0 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-3\"");
510 0 : else if (psElement->style == DGNS_DASH_DOUBLE_DOT)
511 0 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-7\"");
512 0 : else if (psElement->style == DGNS_LONG_DASH_SHORT_DASH)
513 0 : snprintf(szPen, sizeof(szPen), "PEN(p:\"10px 5px 4px 5px\"");
514 : else
515 0 : snprintf(szPen, sizeof(szPen), "PEN(id:\"ogr-pen-0\"");
516 :
517 258 : if (strlen(szFSColor) > 0)
518 258 : snprintf(szPen + strlen(szPen), sizeof(szPen) - strlen(szPen), ",%s",
519 : szFSColor);
520 :
521 258 : if (psElement->weight > 1)
522 0 : snprintf(szPen + strlen(szPen), sizeof(szPen) - strlen(szPen),
523 : ",w:%dpx", psElement->weight);
524 :
525 258 : strcat(szPen, ")");
526 :
527 258 : switch (psElement->stype)
528 : {
529 26 : case DGNST_MULTIPOINT:
530 26 : if (psElement->type == DGNT_SHAPE)
531 : {
532 9 : OGRLinearRing *poLine = new OGRLinearRing();
533 9 : DGNElemMultiPoint *psEMP =
534 : reinterpret_cast<DGNElemMultiPoint *>(psElement);
535 :
536 9 : poLine->setNumPoints(psEMP->num_vertices);
537 54 : for (int i = 0; i < psEMP->num_vertices; i++)
538 : {
539 45 : poLine->setPoint(i, psEMP->vertices[i].x,
540 : psEMP->vertices[i].y,
541 : psEMP->vertices[i].z);
542 : }
543 :
544 9 : OGRPolygon *poPolygon = new OGRPolygon();
545 9 : poPolygon->addRingDirectly(poLine);
546 :
547 9 : poFeature->SetGeometryDirectly(poPolygon);
548 :
549 9 : ConsiderBrush(psElement, szPen, poFeature);
550 : }
551 17 : else if (psElement->type == DGNT_CURVE)
552 : {
553 0 : OGRLineString *poLine = new OGRLineString();
554 0 : DGNElemMultiPoint *psEMP =
555 : reinterpret_cast<DGNElemMultiPoint *>(psElement);
556 0 : const int nPoints = 5 * psEMP->num_vertices;
557 : DGNPoint *pasPoints = static_cast<DGNPoint *>(
558 0 : CPLMalloc(sizeof(DGNPoint) * nPoints));
559 :
560 0 : DGNStrokeCurve(hDGN, psEMP, nPoints, pasPoints);
561 :
562 0 : poLine->setNumPoints(nPoints);
563 0 : for (int i = 0; i < nPoints; i++)
564 : {
565 0 : poLine->setPoint(i, pasPoints[i].x, pasPoints[i].y,
566 0 : pasPoints[i].z);
567 : }
568 :
569 0 : poFeature->SetGeometryDirectly(poLine);
570 0 : CPLFree(pasPoints);
571 :
572 0 : poFeature->SetStyleString(szPen);
573 : }
574 : else
575 : {
576 17 : DGNElemMultiPoint *psEMP = (DGNElemMultiPoint *)psElement;
577 :
578 17 : if (psEMP->num_vertices > 0)
579 : {
580 17 : OGRLineString *poLine = new OGRLineString();
581 17 : poLine->setNumPoints(psEMP->num_vertices);
582 121 : for (int i = 0; i < psEMP->num_vertices; i++)
583 : {
584 104 : poLine->setPoint(i, psEMP->vertices[i].x,
585 : psEMP->vertices[i].y,
586 : psEMP->vertices[i].z);
587 : }
588 :
589 17 : poFeature->SetGeometryDirectly(poLine);
590 : }
591 :
592 17 : poFeature->SetStyleString(szPen);
593 : }
594 26 : break;
595 :
596 6 : case DGNST_ARC:
597 : {
598 6 : DGNElemArc *psArc = (DGNElemArc *)psElement;
599 : int nPoints = static_cast<int>(
600 6 : std::max(1.0, std::abs(psArc->sweepang) / 5.0) + 1.0);
601 6 : if (nPoints > 90)
602 0 : nPoints = 90;
603 :
604 6 : DGNPoint asPoints[90] = {};
605 6 : DGNStrokeArc(hDGN, psArc, nPoints, asPoints);
606 :
607 6 : OGRLineString *poLine = new OGRLineString();
608 6 : poLine->setNumPoints(nPoints);
609 444 : for (int i = 0; i < nPoints; i++)
610 : {
611 438 : poLine->setPoint(i, asPoints[i].x, asPoints[i].y,
612 : asPoints[i].z);
613 : }
614 :
615 6 : poFeature->SetGeometryDirectly(poLine);
616 6 : poFeature->SetStyleString(szPen);
617 : }
618 6 : break;
619 :
620 7 : case DGNST_TEXT:
621 : {
622 7 : OGRPoint *poPoint = new OGRPoint();
623 7 : DGNElemText *psText = reinterpret_cast<DGNElemText *>(psElement);
624 :
625 7 : poPoint->setX(psText->origin.x);
626 7 : poPoint->setY(psText->origin.y);
627 7 : poPoint->setZ(psText->origin.z);
628 :
629 7 : poFeature->SetGeometryDirectly(poPoint);
630 :
631 7 : const size_t nOgrFSLen = strlen(psText->string) + 150;
632 7 : char *pszOgrFS = static_cast<char *>(CPLMalloc(nOgrFSLen));
633 :
634 : // setup the basic label.
635 7 : snprintf(pszOgrFS, nOgrFSLen, "LABEL(t:\"%s\"", psText->string);
636 :
637 : // set the color if we have it.
638 7 : if (strlen(szFSColor) > 0)
639 7 : snprintf(pszOgrFS + strlen(pszOgrFS),
640 7 : nOgrFSLen - strlen(pszOgrFS), ",%s", szFSColor);
641 :
642 : // Add the size info in ground units.
643 : // TODO: std::abs
644 7 : if (std::abs(psText->height_mult) >= 6.0)
645 0 : CPLsnprintf(pszOgrFS + strlen(pszOgrFS),
646 0 : nOgrFSLen - strlen(pszOgrFS), ",s:%dg",
647 0 : static_cast<int>(psText->height_mult));
648 7 : else if (std::abs(psText->height_mult) > 0.1)
649 7 : CPLsnprintf(pszOgrFS + strlen(pszOgrFS),
650 7 : nOgrFSLen - strlen(pszOgrFS), ",s:%.3fg",
651 : psText->height_mult);
652 : else
653 0 : CPLsnprintf(pszOgrFS + strlen(pszOgrFS),
654 0 : nOgrFSLen - strlen(pszOgrFS), ",s:%.12fg",
655 : psText->height_mult);
656 :
657 : // Add the font name. Name it MstnFont<FONTNUMBER> if not available
658 : // in the font list. #3392
659 : static const char *const papszFontList[] = {
660 : "STANDARD",
661 : "WORKING",
662 : "FANCY",
663 : "ENGINEERING",
664 : "NEWZERO",
665 : "STENCEL", // 0-5
666 : "USTN_FANCY",
667 : "COMPRESSED",
668 : "STENCEQ",
669 : nullptr,
670 : "hand",
671 : "ARCH", // 6-11
672 : "ARCHB",
673 : nullptr,
674 : nullptr,
675 : "IGES1001",
676 : "IGES1002",
677 : "IGES1003", // 12-17
678 : "CENTB",
679 : "MICROS",
680 : nullptr,
681 : nullptr,
682 : "ISOFRACTIONS",
683 : "ITALICS", // 18-23
684 : "ISO30",
685 : nullptr,
686 : "GREEK",
687 : "ISOREC",
688 : "Isoeq",
689 : nullptr, // 24-29
690 : "ISO_FONTLEFT",
691 : "ISO_FONTRIGHT",
692 : "INTL_ENGINEERING",
693 : "INTL_WORKING",
694 : "ISOITEQ",
695 : nullptr, // 30-35
696 : "USTN FONT 26",
697 : nullptr,
698 : nullptr,
699 : nullptr,
700 : nullptr,
701 : "ARCHITECTURAL", // 36-41
702 : "BLOCK_OUTLINE",
703 : "LOW_RES_FILLED",
704 : nullptr,
705 : nullptr,
706 : nullptr,
707 : nullptr, // 42-47
708 : nullptr,
709 : nullptr,
710 : "UPPERCASE",
711 : nullptr,
712 : nullptr,
713 : nullptr, // 48-53
714 : nullptr,
715 : nullptr,
716 : nullptr,
717 : nullptr,
718 : nullptr,
719 : nullptr, // 54-49
720 : "FONT060",
721 : "din",
722 : "dinit",
723 : "helvl",
724 : "HELVLIT",
725 : "helv", // 60-65
726 : "HELVIT",
727 : "cent",
728 : "CENTIT",
729 : "SCRIPT",
730 : nullptr,
731 : nullptr, // 66-71
732 : nullptr,
733 : nullptr,
734 : nullptr,
735 : nullptr,
736 : "MICROQ",
737 : "dotfont", // 72-77
738 : "DOTIT",
739 : nullptr,
740 : nullptr,
741 : nullptr,
742 : nullptr,
743 : nullptr, // 78-83
744 : nullptr,
745 : nullptr,
746 : nullptr,
747 : nullptr,
748 : nullptr,
749 : nullptr, // 84-89
750 : nullptr,
751 : nullptr,
752 : "FONT092",
753 : nullptr,
754 : "FONT094",
755 : nullptr, // 90-95
756 : nullptr,
757 : nullptr,
758 : nullptr,
759 : nullptr,
760 : "ANSI_SYMBOLS",
761 : "FEATURE_CONTROL_SYSMBOLS", // 96-101
762 : "SYMB_FAST",
763 : nullptr,
764 : nullptr,
765 : "INTL_ISO",
766 : "INTL_ISO_EQUAL",
767 : "INTL_ISO_ITALIC", // 102-107
768 : "INTL_ISO_ITALIC_EQUAL"}; // 108
769 :
770 7 : if (psText->font_id <= 108 &&
771 7 : papszFontList[psText->font_id] != nullptr)
772 : {
773 7 : snprintf(pszOgrFS + strlen(pszOgrFS),
774 7 : nOgrFSLen - strlen(pszOgrFS), ",f:%s",
775 7 : papszFontList[psText->font_id]);
776 : }
777 : else
778 : {
779 0 : snprintf(pszOgrFS + strlen(pszOgrFS),
780 0 : nOgrFSLen - strlen(pszOgrFS), ",f:MstnFont%d",
781 : psText->font_id);
782 : }
783 :
784 : // Add the angle, if not horizontal
785 7 : if (psText->rotation != 0.0)
786 0 : snprintf(pszOgrFS + strlen(pszOgrFS),
787 0 : nOgrFSLen - strlen(pszOgrFS), ",a:%d",
788 0 : (int)(psText->rotation + 0.5));
789 :
790 7 : snprintf(pszOgrFS + strlen(pszOgrFS), nOgrFSLen - strlen(pszOgrFS),
791 : ")");
792 :
793 7 : poFeature->SetStyleString(pszOgrFS);
794 7 : CPLFree(pszOgrFS);
795 :
796 7 : poFeature->SetField("Text", psText->string);
797 : }
798 7 : break;
799 :
800 1 : case DGNST_COMPLEX_HEADER:
801 : {
802 1 : DGNElemComplexHeader *psHdr = (DGNElemComplexHeader *)psElement;
803 2 : OGRMultiLineString oChildren;
804 :
805 : /* collect subsequent child geometries. */
806 : // we should disable the spatial filter ... add later.
807 3 : for (int iChild = 0; iChild < psHdr->numelems && nRecLevel < 20;
808 : iChild++)
809 : {
810 2 : OGRFeature *poChildFeature = nullptr;
811 2 : DGNElemCore *psChildElement = DGNReadElement(hDGN);
812 : // should verify complex bit set, not another header.
813 :
814 2 : if (psChildElement != nullptr)
815 : {
816 : poChildFeature =
817 2 : ElementToFeature(psChildElement, nRecLevel + 1);
818 2 : DGNFreeElement(hDGN, psChildElement);
819 : }
820 :
821 4 : if (poChildFeature != nullptr &&
822 2 : poChildFeature->GetGeometryRef() != nullptr)
823 : {
824 2 : OGRGeometry *poGeom = poChildFeature->GetGeometryRef();
825 2 : if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
826 2 : oChildren.addGeometry(poGeom);
827 : }
828 :
829 2 : if (poChildFeature != nullptr)
830 2 : delete poChildFeature;
831 : }
832 :
833 : // Try to assemble into polygon geometry.
834 1 : OGRGeometry *poGeom = nullptr;
835 :
836 1 : if (psElement->type == DGNT_COMPLEX_SHAPE_HEADER)
837 0 : poGeom = OGRGeometry::FromHandle(
838 : OGRBuildPolygonFromEdges(OGRGeometry::ToHandle(&oChildren),
839 : TRUE, TRUE, 100000, nullptr));
840 : else
841 1 : poGeom = oChildren.clone();
842 :
843 1 : if (poGeom != nullptr)
844 1 : poFeature->SetGeometryDirectly(poGeom);
845 :
846 1 : ConsiderBrush(psElement, szPen, poFeature);
847 : }
848 1 : break;
849 :
850 218 : default:
851 218 : break;
852 : }
853 :
854 : /* -------------------------------------------------------------------- */
855 : /* Fixup geometry dimension. */
856 : /* -------------------------------------------------------------------- */
857 258 : if (poFeature->GetGeometryRef() != nullptr)
858 80 : poFeature->GetGeometryRef()->setCoordinateDimension(
859 40 : DGNGetDimension(hDGN));
860 :
861 516 : return poFeature;
862 : }
863 :
864 : /************************************************************************/
865 : /* GetNextFeature() */
866 : /************************************************************************/
867 :
868 35 : OGRFeature *OGRDGNLayer::GetNextFeature()
869 :
870 : {
871 35 : DGNGetElementIndex(hDGN, nullptr);
872 :
873 35 : DGNElemCore *psElement = nullptr;
874 259 : while ((psElement = DGNReadElement(hDGN)) != nullptr)
875 : {
876 256 : if (psElement->deleted)
877 : {
878 0 : DGNFreeElement(hDGN, psElement);
879 0 : continue;
880 : }
881 :
882 256 : OGRFeature *poFeature = ElementToFeature(psElement, 0);
883 256 : DGNFreeElement(hDGN, psElement);
884 :
885 256 : if (poFeature == nullptr)
886 0 : continue;
887 :
888 256 : if (poFeature->GetGeometryRef() == nullptr)
889 : {
890 218 : delete poFeature;
891 218 : continue;
892 : }
893 :
894 70 : if ((m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)) &&
895 32 : FilterGeometry(poFeature->GetGeometryRef()))
896 32 : return poFeature;
897 :
898 6 : delete poFeature;
899 : }
900 :
901 3 : return nullptr;
902 : }
903 :
904 : /************************************************************************/
905 : /* TestCapability() */
906 : /************************************************************************/
907 :
908 68 : int OGRDGNLayer::TestCapability(const char *pszCap)
909 :
910 : {
911 68 : if (EQUAL(pszCap, OLCRandomRead))
912 0 : return TRUE;
913 :
914 68 : else if (EQUAL(pszCap, OLCSequentialWrite))
915 16 : return bUpdate;
916 52 : else if (EQUAL(pszCap, OLCRandomWrite))
917 0 : return FALSE; /* maybe later? */
918 :
919 52 : else if (EQUAL(pszCap, OLCFastFeatureCount))
920 0 : return m_poFilterGeom == nullptr || m_poAttrQuery == nullptr;
921 :
922 52 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
923 0 : return FALSE;
924 :
925 52 : else if (EQUAL(pszCap, OLCFastGetExtent))
926 0 : return TRUE;
927 :
928 52 : else if (EQUAL(pszCap, OLCZGeometries))
929 0 : return TRUE;
930 :
931 52 : return FALSE;
932 : }
933 :
934 : /************************************************************************/
935 : /* GetFeatureCount() */
936 : /************************************************************************/
937 :
938 0 : GIntBig OGRDGNLayer::GetFeatureCount(int bForce)
939 :
940 : {
941 : /* -------------------------------------------------------------------- */
942 : /* If any odd conditions are in effect collect the information */
943 : /* normally. */
944 : /* -------------------------------------------------------------------- */
945 0 : if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
946 0 : return OGRLayer::GetFeatureCount(bForce);
947 :
948 : /* -------------------------------------------------------------------- */
949 : /* Otherwise scan the index. */
950 : /* -------------------------------------------------------------------- */
951 0 : int nElementCount = 0;
952 0 : const DGNElementInfo *pasIndex = DGNGetElementIndex(hDGN, &nElementCount);
953 :
954 0 : int nFeatureCount = 0;
955 0 : bool bInComplexShape = false;
956 :
957 0 : for (int i = 0; i < nElementCount; i++)
958 : {
959 0 : if (pasIndex[i].flags & DGNEIF_DELETED)
960 0 : continue;
961 :
962 0 : switch (pasIndex[i].stype)
963 : {
964 0 : case DGNST_MULTIPOINT:
965 : case DGNST_ARC:
966 : case DGNST_TEXT:
967 0 : if (!(pasIndex[i].flags & DGNEIF_COMPLEX) || !bInComplexShape)
968 : {
969 0 : nFeatureCount++;
970 0 : bInComplexShape = false;
971 : }
972 0 : break;
973 :
974 0 : case DGNST_COMPLEX_HEADER:
975 0 : nFeatureCount++;
976 0 : bInComplexShape = true;
977 0 : break;
978 :
979 0 : default:
980 0 : break;
981 : }
982 : }
983 :
984 0 : return nFeatureCount;
985 : }
986 :
987 : /************************************************************************/
988 : /* GetExtent() */
989 : /************************************************************************/
990 :
991 0 : OGRErr OGRDGNLayer::GetExtent(OGREnvelope *psExtent, int /* bForce */)
992 : {
993 : double adfExtents[6];
994 :
995 0 : if (!DGNGetExtents(hDGN, adfExtents))
996 0 : return OGRERR_FAILURE;
997 :
998 0 : psExtent->MinX = adfExtents[0];
999 0 : psExtent->MinY = adfExtents[1];
1000 0 : psExtent->MaxX = adfExtents[3];
1001 0 : psExtent->MaxY = adfExtents[4];
1002 :
1003 0 : return OGRERR_NONE;
1004 : }
1005 :
1006 : /************************************************************************/
1007 : /* LineStringToElementGroup() */
1008 : /* */
1009 : /* Convert an OGR line string to one or more DGN elements. If */
1010 : /* the input is too long for a single element (more than 38 */
1011 : /* points) we split it into multiple LINE_STRING elements, and */
1012 : /* prefix with a complex group header element. */
1013 : /* */
1014 : /* This method can create handle creating shapes, or line */
1015 : /* strings for the aggregate object, but the components of a */
1016 : /* complex shape group are always line strings. */
1017 : /************************************************************************/
1018 :
1019 : constexpr int MAX_ELEM_POINTS = 38;
1020 :
1021 29 : DGNElemCore **OGRDGNLayer::LineStringToElementGroup(const OGRLineString *poLS,
1022 : int nGroupType)
1023 :
1024 : {
1025 29 : const int nTotalPoints = poLS->getNumPoints();
1026 29 : int iGeom = 0;
1027 : DGNElemCore **papsGroup = static_cast<DGNElemCore **>(
1028 29 : CPLCalloc(sizeof(void *), (nTotalPoints / (MAX_ELEM_POINTS - 1)) + 3));
1029 :
1030 59 : for (int iNextPoint = 0; iNextPoint < nTotalPoints;)
1031 : {
1032 30 : DGNPoint asPoints[MAX_ELEM_POINTS] = {};
1033 30 : int nThisCount = 0;
1034 :
1035 : // we need to repeat end points of elements.
1036 : // cppcheck-suppress duplicateExpression
1037 30 : if (iNextPoint != 0)
1038 1 : iNextPoint--;
1039 :
1040 199 : for (; iNextPoint < nTotalPoints && nThisCount < MAX_ELEM_POINTS;
1041 : iNextPoint++, nThisCount++)
1042 : {
1043 169 : asPoints[nThisCount].x = poLS->getX(iNextPoint);
1044 169 : asPoints[nThisCount].y = poLS->getY(iNextPoint);
1045 169 : asPoints[nThisCount].z = poLS->getZ(iNextPoint);
1046 : }
1047 :
1048 30 : if (nTotalPoints <= MAX_ELEM_POINTS)
1049 28 : papsGroup[0] =
1050 28 : DGNCreateMultiPointElem(hDGN, nGroupType, nThisCount, asPoints);
1051 : else
1052 2 : papsGroup[++iGeom] = DGNCreateMultiPointElem(hDGN, DGNT_LINE_STRING,
1053 : nThisCount, asPoints);
1054 : }
1055 :
1056 : /* -------------------------------------------------------------------- */
1057 : /* We needed to make into a group. Create the complex header */
1058 : /* from the rest of the group. */
1059 : /* -------------------------------------------------------------------- */
1060 29 : if (papsGroup[0] == nullptr)
1061 : {
1062 1 : if (nGroupType == DGNT_SHAPE)
1063 0 : nGroupType = DGNT_COMPLEX_SHAPE_HEADER;
1064 : else
1065 1 : nGroupType = DGNT_COMPLEX_CHAIN_HEADER;
1066 :
1067 1 : papsGroup[0] = DGNCreateComplexHeaderFromGroup(hDGN, nGroupType, iGeom,
1068 : papsGroup + 1);
1069 : }
1070 :
1071 29 : return papsGroup;
1072 : }
1073 :
1074 : /************************************************************************/
1075 : /* TranslateLabel() */
1076 : /* */
1077 : /* Translate LABEL feature. */
1078 : /************************************************************************/
1079 :
1080 1 : DGNElemCore **OGRDGNLayer::TranslateLabel(OGRFeature *poFeature)
1081 :
1082 : {
1083 1 : OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
1084 1 : const char *pszText = poFeature->GetFieldAsString("Text");
1085 :
1086 1 : OGRStyleMgr oMgr;
1087 1 : oMgr.InitFromFeature(poFeature);
1088 1 : OGRStyleLabel *poLabel = reinterpret_cast<OGRStyleLabel *>(oMgr.GetPart(0));
1089 1 : if (poLabel != nullptr && poLabel->GetType() != OGRSTCLabel)
1090 : {
1091 0 : delete poLabel;
1092 0 : poLabel = nullptr;
1093 : }
1094 :
1095 1 : double dfRotation = 0.0;
1096 1 : double dfCharHeight = 100.0;
1097 1 : int nFontID = 1; // 1 is the default font for DGN. Not 0.
1098 :
1099 1 : if (poLabel != nullptr)
1100 : {
1101 : GBool bDefault;
1102 :
1103 1 : if (poLabel->TextString(bDefault) != nullptr && !bDefault)
1104 1 : pszText = poLabel->TextString(bDefault);
1105 1 : dfRotation = poLabel->Angle(bDefault);
1106 :
1107 1 : poLabel->Size(bDefault);
1108 1 : if (!bDefault && poLabel->GetUnit() == OGRSTUGround)
1109 0 : dfCharHeight = poLabel->Size(bDefault);
1110 : // this part is really kind of bogus.
1111 1 : if (!bDefault && poLabel->GetUnit() == OGRSTUMM)
1112 1 : dfCharHeight = poLabel->Size(bDefault) / 1000.0;
1113 :
1114 : /* get font id */
1115 : static const char *const papszFontNumbers[] = {
1116 : "STANDARD=0",
1117 : "WORKING=1",
1118 : "FANCY=2",
1119 : "ENGINEERING=3",
1120 : "NEWZERO=4",
1121 : "STENCEL=5",
1122 : "USTN_FANCY=7",
1123 : "COMPRESSED=8",
1124 : "STENCEQ=9",
1125 : "hand=10",
1126 : "ARCH=11",
1127 : "ARCHB=12",
1128 : "IGES1001=15",
1129 : "IGES1002=16",
1130 : "IGES1003=17",
1131 : "CENTB=18",
1132 : "MICROS=19",
1133 : "ISOFRACTIONS=22",
1134 : "ITALICS=23",
1135 : "ISO30=24",
1136 : "GREEK=25",
1137 : "ISOREC=26",
1138 : "Isoeq=27",
1139 : "ISO_FONTLEFT=30",
1140 : "ISO_FONTRIGHT=31",
1141 : "INTL_ENGINEERING=32",
1142 : "INTL_WORKING=33",
1143 : "ISOITEQ=34",
1144 : "USTN FONT 26=36",
1145 : "ARCHITECTURAL=41",
1146 : "BLOCK_OUTLINE=42",
1147 : "LOW_RES_FILLED=43",
1148 : "UPPERCASE50",
1149 : "FONT060=60",
1150 : "din=61",
1151 : "dinit=62",
1152 : "helvl=63",
1153 : "HELVLIT=64",
1154 : "helv=65",
1155 : "HELVIT=66",
1156 : "cent=67",
1157 : "CENTIT=68",
1158 : "SCRIPT=69",
1159 : "MICROQ=76",
1160 : "dotfont=77",
1161 : "DOTIT=78",
1162 : "FONT092=92",
1163 : "FONT094=94",
1164 : "ANSI_SYMBOLS=100",
1165 : "FEATURE_CONTROL_SYSMBOLS=101",
1166 : "SYMB_FAST=102",
1167 : "INTL_ISO=105",
1168 : "INTL_ISO_EQUAL=106",
1169 : "INTL_ISO_ITALIC=107",
1170 : "INTL_ISO_ITALIC_EQUAL=108",
1171 : nullptr};
1172 :
1173 1 : const char *pszFontName = poLabel->FontName(bDefault);
1174 1 : if (!bDefault && pszFontName != nullptr)
1175 : {
1176 1 : const char *pszFontNumber = CSLFetchNameValue(
1177 : const_cast<char **>(papszFontNumbers), pszFontName);
1178 :
1179 1 : if (pszFontNumber != nullptr)
1180 : {
1181 1 : nFontID = atoi(pszFontNumber);
1182 : }
1183 : }
1184 : }
1185 :
1186 : DGNElemCore **papsGroup =
1187 1 : static_cast<DGNElemCore **>(CPLCalloc(sizeof(void *), 2));
1188 1 : papsGroup[0] = DGNCreateTextElem(
1189 : hDGN, pszText, nFontID, DGNJ_LEFT_BOTTOM, dfCharHeight, dfCharHeight,
1190 : dfRotation, nullptr, poPoint->getX(), poPoint->getY(), poPoint->getZ());
1191 :
1192 1 : if (poLabel)
1193 1 : delete poLabel;
1194 :
1195 2 : return papsGroup;
1196 : }
1197 :
1198 : /************************************************************************/
1199 : /* ICreateFeature() */
1200 : /* */
1201 : /* Create a new feature and write to file. */
1202 : /************************************************************************/
1203 :
1204 71 : OGRErr OGRDGNLayer::ICreateFeature(OGRFeature *poFeature)
1205 :
1206 : {
1207 71 : if (!bUpdate)
1208 : {
1209 0 : CPLError(CE_Failure, CPLE_AppDefined,
1210 : "Attempt to create feature on read-only DGN file.");
1211 0 : return OGRERR_FAILURE;
1212 : }
1213 :
1214 71 : return CreateFeatureWithGeom(poFeature, poFeature->GetGeometryRef());
1215 : }
1216 :
1217 : /************************************************************************/
1218 : /* CreateFeatureWithGeom() */
1219 : /* */
1220 : /* Create an element or element group from a given geometry and */
1221 : /* the given feature. This method recurses to handle */
1222 : /* collections as essentially independent features. */
1223 : /************************************************************************/
1224 :
1225 97 : OGRErr OGRDGNLayer::CreateFeatureWithGeom(OGRFeature *poFeature,
1226 : const OGRGeometry *poGeom)
1227 :
1228 : {
1229 :
1230 97 : if (poGeom == nullptr || poGeom->IsEmpty())
1231 : {
1232 36 : CPLError(CE_Failure, CPLE_AppDefined,
1233 : "Features with empty, geometry collection geometries not\n"
1234 : "supported in DGN format.");
1235 36 : return OGRERR_FAILURE;
1236 : }
1237 :
1238 : /* -------------------------------------------------------------------- */
1239 : /* Translate the geometry. */
1240 : /* -------------------------------------------------------------------- */
1241 61 : DGNElemCore **papsGroup = nullptr;
1242 61 : const char *pszStyle = poFeature->GetStyleString();
1243 :
1244 61 : if (wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1245 : {
1246 15 : const OGRPoint *poPoint = poGeom->toPoint();
1247 15 : const char *pszText = poFeature->GetFieldAsString("Text");
1248 :
1249 15 : if ((pszText == nullptr || strlen(pszText) == 0) &&
1250 0 : (pszStyle == nullptr || strstr(pszStyle, "LABEL") == nullptr))
1251 : {
1252 : // Treat a non text point as a degenerate line.
1253 14 : DGNPoint asPoints[2] = {};
1254 14 : asPoints[0].x = poPoint->getX();
1255 14 : asPoints[0].y = poPoint->getY();
1256 14 : asPoints[0].z = poPoint->getZ();
1257 14 : asPoints[1] = asPoints[0];
1258 :
1259 : papsGroup =
1260 14 : static_cast<DGNElemCore **>(CPLCalloc(sizeof(void *), 2));
1261 14 : papsGroup[0] =
1262 14 : DGNCreateMultiPointElem(hDGN, DGNT_LINE, 2, asPoints);
1263 : }
1264 : else
1265 : {
1266 1 : papsGroup = TranslateLabel(poFeature);
1267 : }
1268 : }
1269 46 : else if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
1270 : {
1271 : papsGroup =
1272 16 : LineStringToElementGroup(poGeom->toLineString(), DGNT_LINE_STRING);
1273 : }
1274 30 : else if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
1275 : {
1276 13 : const OGRPolygon *poPoly = poGeom->toPolygon();
1277 :
1278 : DGNElemCore **papsGroupExt =
1279 13 : LineStringToElementGroup(poPoly->getExteriorRing(), DGNT_SHAPE);
1280 :
1281 13 : const int innerRingsCnt = poPoly->getNumInteriorRings();
1282 :
1283 13 : if (innerRingsCnt > 0)
1284 : {
1285 0 : CPLDebug("InnerRings", "there are %d inner rings", innerRingsCnt);
1286 0 : std::list<DGNElemCore *> dgnElements;
1287 :
1288 0 : for (int i = 0; papsGroupExt[i] != nullptr; i++)
1289 : {
1290 0 : dgnElements.push_back(papsGroupExt[i]);
1291 : }
1292 0 : CPLFree(papsGroupExt);
1293 :
1294 : // get all interior rings and create complex group shape
1295 0 : for (int iRing = 0; iRing < innerRingsCnt; iRing++)
1296 : {
1297 0 : DGNElemCore **papsGroupInner = LineStringToElementGroup(
1298 0 : poPoly->getInteriorRing(iRing), DGNT_SHAPE);
1299 0 : papsGroupInner[0]->properties |= DGNPF_HOLE;
1300 0 : DGNUpdateElemCoreExtended(hDGN, papsGroupInner[0]);
1301 0 : for (int i = 0; papsGroupInner[i] != nullptr; i++)
1302 : {
1303 0 : dgnElements.push_back(papsGroupInner[i]);
1304 : }
1305 0 : CPLFree(papsGroupInner);
1306 : }
1307 0 : int index = 1;
1308 0 : papsGroup = (DGNElemCore **)CPLCalloc(sizeof(void *),
1309 0 : dgnElements.size() + 2);
1310 0 : for (std::list<DGNElemCore *>::iterator list_iter =
1311 0 : dgnElements.begin();
1312 0 : list_iter != dgnElements.end(); ++list_iter)
1313 : {
1314 0 : papsGroup[index++] = *list_iter;
1315 : }
1316 :
1317 : // papsGroup[0] = DGNCreateComplexHeaderFromGroup(
1318 : // hDGN, DGNT_COMPLEX_SHAPE_HEADER, dgnElements.size(),
1319 : // papsGroup+1 );
1320 0 : DGNPoint asPoints[1] = {};
1321 0 : papsGroup[0] = DGNCreateCellHeaderFromGroup(
1322 0 : hDGN, "", 1, nullptr, static_cast<int>(dgnElements.size()),
1323 : papsGroup + 1, asPoints + 0, 1.0, 1.0, 0.0);
1324 0 : DGNAddShapeFillInfo(hDGN, papsGroup[0], 6);
1325 : }
1326 : else
1327 : {
1328 13 : papsGroup = papsGroupExt;
1329 : }
1330 : }
1331 17 : else if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ||
1332 13 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint ||
1333 35 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ||
1334 5 : wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
1335 : {
1336 42 : for (auto &&poMember : poGeom->toGeometryCollection())
1337 : {
1338 26 : OGRErr eErr = CreateFeatureWithGeom(poFeature, poMember);
1339 26 : if (eErr != OGRERR_NONE)
1340 1 : return eErr;
1341 : }
1342 :
1343 16 : return OGRERR_NONE;
1344 : }
1345 : else
1346 : {
1347 0 : CPLError(CE_Failure, CPLE_AppDefined,
1348 : "Unsupported geometry type (%s) for DGN.",
1349 0 : OGRGeometryTypeToName(poGeom->getGeometryType()));
1350 0 : return OGRERR_FAILURE;
1351 : }
1352 :
1353 : /* -------------------------------------------------------------------- */
1354 : /* Add other attributes. */
1355 : /* -------------------------------------------------------------------- */
1356 44 : int nLevel = poFeature->GetFieldAsInteger("Level");
1357 44 : int nGraphicGroup = poFeature->GetFieldAsInteger("GraphicGroup");
1358 44 : int nColor = poFeature->GetFieldAsInteger("ColorIndex");
1359 44 : int nWeight = poFeature->GetFieldAsInteger("Weight");
1360 44 : int nStyle = poFeature->GetFieldAsInteger("Style");
1361 44 : int nMSLink = poFeature->GetFieldAsInteger("MSLink");
1362 :
1363 : // TODO: Use std::max and std::min.
1364 44 : nLevel = std::max(0, std::min(63, nLevel));
1365 44 : nColor = std::max(0, std::min(255, nColor));
1366 44 : nWeight = std::max(0, std::min(31, nWeight));
1367 44 : nStyle = std::max(0, std::min(7, nStyle));
1368 44 : nMSLink = std::max(0, nMSLink);
1369 :
1370 44 : DGNUpdateElemCore(hDGN, papsGroup[0], nLevel, nGraphicGroup, nColor,
1371 : nWeight, nStyle);
1372 44 : DGNAddMSLink(hDGN, papsGroup[0], DGNLT_ODBC, 0, nMSLink);
1373 : /* -------------------------------------------------------------------- */
1374 : /* Write to file. */
1375 : /* -------------------------------------------------------------------- */
1376 90 : for (int i = 0; papsGroup[i] != nullptr; i++)
1377 : {
1378 46 : DGNWriteElement(hDGN, papsGroup[i]);
1379 :
1380 46 : if (i == 0)
1381 44 : poFeature->SetFID(papsGroup[i]->element_id);
1382 :
1383 46 : DGNFreeElement(hDGN, papsGroup[i]);
1384 : }
1385 :
1386 44 : CPLFree(papsGroup);
1387 :
1388 44 : return OGRERR_NONE;
1389 : }
|