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