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