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 = (DGNElemMultiPoint *)psElement;
562 :
563 17 : if (psEMP->num_vertices > 0)
564 : {
565 17 : OGRLineString *poLine = new OGRLineString();
566 17 : poLine->setNumPoints(psEMP->num_vertices);
567 121 : for (int i = 0; i < psEMP->num_vertices; i++)
568 : {
569 104 : poLine->setPoint(i, psEMP->vertices[i].x,
570 : psEMP->vertices[i].y,
571 : psEMP->vertices[i].z);
572 : }
573 :
574 17 : poFeature->SetGeometryDirectly(poLine);
575 : }
576 :
577 17 : poFeature->SetStyleString(szPen);
578 : }
579 26 : break;
580 :
581 6 : case DGNST_ARC:
582 : {
583 6 : DGNElemArc *psArc = (DGNElemArc *)psElement;
584 : int nPoints = static_cast<int>(
585 6 : std::max(1.0, std::abs(psArc->sweepang) / 5.0) + 1.0);
586 6 : if (nPoints > 90)
587 0 : nPoints = 90;
588 :
589 6 : DGNPoint asPoints[90] = {};
590 6 : DGNStrokeArc(hDGN, psArc, nPoints, asPoints);
591 :
592 6 : OGRLineString *poLine = new OGRLineString();
593 6 : poLine->setNumPoints(nPoints);
594 444 : for (int i = 0; i < nPoints; i++)
595 : {
596 438 : poLine->setPoint(i, asPoints[i].x, asPoints[i].y,
597 : asPoints[i].z);
598 : }
599 :
600 6 : poFeature->SetGeometryDirectly(poLine);
601 6 : poFeature->SetStyleString(szPen);
602 : }
603 6 : break;
604 :
605 9 : case DGNST_TEXT:
606 : {
607 9 : OGRPoint *poPoint = new OGRPoint();
608 9 : DGNElemText *psText = reinterpret_cast<DGNElemText *>(psElement);
609 :
610 9 : poPoint->setX(psText->origin.x);
611 9 : poPoint->setY(psText->origin.y);
612 9 : poPoint->setZ(psText->origin.z);
613 :
614 9 : poFeature->SetGeometryDirectly(poPoint);
615 :
616 9 : const auto &osEncoding = m_poDS->GetEncoding();
617 18 : std::string osText;
618 9 : if (!osEncoding.empty() && osEncoding != CPL_ENC_UTF8)
619 : {
620 2 : osText = CPLString(psText->string)
621 1 : .Recode(osEncoding.c_str(), CPL_ENC_UTF8);
622 : }
623 : else
624 : {
625 8 : osText = psText->string;
626 : }
627 :
628 9 : const size_t nOgrFSLen = osText.size() + 150;
629 9 : char *pszOgrFS = static_cast<char *>(CPLMalloc(nOgrFSLen));
630 :
631 : // setup the basic label.
632 9 : snprintf(pszOgrFS, nOgrFSLen, "LABEL(t:\"%s\"", osText.c_str());
633 :
634 : // set the color if we have it.
635 9 : if (strlen(szFSColor) > 0)
636 9 : snprintf(pszOgrFS + strlen(pszOgrFS),
637 9 : nOgrFSLen - strlen(pszOgrFS), ",%s", szFSColor);
638 :
639 : // Add the size info in ground units.
640 : // TODO: std::abs
641 9 : if (std::abs(psText->height_mult) >= 6.0)
642 2 : CPLsnprintf(pszOgrFS + strlen(pszOgrFS),
643 2 : nOgrFSLen - strlen(pszOgrFS), ",s:%dg",
644 2 : static_cast<int>(psText->height_mult));
645 7 : else if (std::abs(psText->height_mult) > 0.1)
646 7 : CPLsnprintf(pszOgrFS + strlen(pszOgrFS),
647 7 : nOgrFSLen - strlen(pszOgrFS), ",s:%.3fg",
648 : psText->height_mult);
649 : else
650 0 : CPLsnprintf(pszOgrFS + strlen(pszOgrFS),
651 0 : nOgrFSLen - strlen(pszOgrFS), ",s:%.12fg",
652 : psText->height_mult);
653 :
654 : // Add the font name. Name it MstnFont<FONTNUMBER> if not available
655 : // in the font list. #3392
656 : static const char *const papszFontList[] = {
657 : "STANDARD",
658 : "WORKING",
659 : "FANCY",
660 : "ENGINEERING",
661 : "NEWZERO",
662 : "STENCEL", // 0-5
663 : "USTN_FANCY",
664 : "COMPRESSED",
665 : "STENCEQ",
666 : nullptr,
667 : "hand",
668 : "ARCH", // 6-11
669 : "ARCHB",
670 : nullptr,
671 : nullptr,
672 : "IGES1001",
673 : "IGES1002",
674 : "IGES1003", // 12-17
675 : "CENTB",
676 : "MICROS",
677 : nullptr,
678 : nullptr,
679 : "ISOFRACTIONS",
680 : "ITALICS", // 18-23
681 : "ISO30",
682 : nullptr,
683 : "GREEK",
684 : "ISOREC",
685 : "Isoeq",
686 : nullptr, // 24-29
687 : "ISO_FONTLEFT",
688 : "ISO_FONTRIGHT",
689 : "INTL_ENGINEERING",
690 : "INTL_WORKING",
691 : "ISOITEQ",
692 : nullptr, // 30-35
693 : "USTN FONT 26",
694 : nullptr,
695 : nullptr,
696 : nullptr,
697 : nullptr,
698 : "ARCHITECTURAL", // 36-41
699 : "BLOCK_OUTLINE",
700 : "LOW_RES_FILLED",
701 : nullptr,
702 : nullptr,
703 : nullptr,
704 : nullptr, // 42-47
705 : nullptr,
706 : nullptr,
707 : "UPPERCASE",
708 : nullptr,
709 : nullptr,
710 : nullptr, // 48-53
711 : nullptr,
712 : nullptr,
713 : nullptr,
714 : nullptr,
715 : nullptr,
716 : nullptr, // 54-49
717 : "FONT060",
718 : "din",
719 : "dinit",
720 : "helvl",
721 : "HELVLIT",
722 : "helv", // 60-65
723 : "HELVIT",
724 : "cent",
725 : "CENTIT",
726 : "SCRIPT",
727 : nullptr,
728 : nullptr, // 66-71
729 : nullptr,
730 : nullptr,
731 : nullptr,
732 : nullptr,
733 : "MICROQ",
734 : "dotfont", // 72-77
735 : "DOTIT",
736 : nullptr,
737 : nullptr,
738 : nullptr,
739 : nullptr,
740 : nullptr, // 78-83
741 : nullptr,
742 : nullptr,
743 : nullptr,
744 : nullptr,
745 : nullptr,
746 : nullptr, // 84-89
747 : nullptr,
748 : nullptr,
749 : "FONT092",
750 : nullptr,
751 : "FONT094",
752 : nullptr, // 90-95
753 : nullptr,
754 : nullptr,
755 : nullptr,
756 : nullptr,
757 : "ANSI_SYMBOLS",
758 : "FEATURE_CONTROL_SYSMBOLS", // 96-101
759 : "SYMB_FAST",
760 : nullptr,
761 : nullptr,
762 : "INTL_ISO",
763 : "INTL_ISO_EQUAL",
764 : "INTL_ISO_ITALIC", // 102-107
765 : "INTL_ISO_ITALIC_EQUAL"}; // 108
766 :
767 9 : if (psText->font_id <= 108 &&
768 9 : papszFontList[psText->font_id] != nullptr)
769 : {
770 9 : snprintf(pszOgrFS + strlen(pszOgrFS),
771 9 : nOgrFSLen - strlen(pszOgrFS), ",f:%s",
772 9 : papszFontList[psText->font_id]);
773 : }
774 : else
775 : {
776 0 : snprintf(pszOgrFS + strlen(pszOgrFS),
777 0 : nOgrFSLen - strlen(pszOgrFS), ",f:MstnFont%d",
778 : psText->font_id);
779 : }
780 :
781 : // Add the angle, if not horizontal
782 9 : if (psText->rotation != 0.0)
783 0 : snprintf(pszOgrFS + strlen(pszOgrFS),
784 0 : nOgrFSLen - strlen(pszOgrFS), ",a:%d",
785 0 : (int)(psText->rotation + 0.5));
786 :
787 9 : snprintf(pszOgrFS + strlen(pszOgrFS), nOgrFSLen - strlen(pszOgrFS),
788 : ")");
789 :
790 9 : poFeature->SetStyleString(pszOgrFS);
791 9 : CPLFree(pszOgrFS);
792 :
793 9 : poFeature->SetField("Text", osText.c_str());
794 : }
795 9 : break;
796 :
797 1 : case DGNST_COMPLEX_HEADER:
798 : {
799 1 : DGNElemComplexHeader *psHdr = (DGNElemComplexHeader *)psElement;
800 2 : OGRMultiLineString oChildren;
801 :
802 : /* collect subsequent child geometries. */
803 : // we should disable the spatial filter ... add later.
804 3 : for (int iChild = 0; iChild < psHdr->numelems && nRecLevel < 20;
805 : iChild++)
806 : {
807 2 : OGRFeature *poChildFeature = nullptr;
808 2 : DGNElemCore *psChildElement = DGNReadElement(hDGN);
809 : // should verify complex bit set, not another header.
810 :
811 2 : if (psChildElement != nullptr)
812 : {
813 : poChildFeature =
814 2 : ElementToFeature(psChildElement, nRecLevel + 1);
815 2 : DGNFreeElement(hDGN, psChildElement);
816 : }
817 :
818 4 : if (poChildFeature != nullptr &&
819 2 : poChildFeature->GetGeometryRef() != nullptr)
820 : {
821 2 : OGRGeometry *poGeom = poChildFeature->GetGeometryRef();
822 2 : if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
823 2 : oChildren.addGeometry(poGeom);
824 : }
825 :
826 2 : if (poChildFeature != nullptr)
827 2 : delete poChildFeature;
828 : }
829 :
830 : // Try to assemble into polygon geometry.
831 1 : OGRGeometry *poGeom = nullptr;
832 :
833 1 : if (psElement->type == DGNT_COMPLEX_SHAPE_HEADER)
834 0 : poGeom = OGRGeometry::FromHandle(
835 : OGRBuildPolygonFromEdges(OGRGeometry::ToHandle(&oChildren),
836 : TRUE, TRUE, 100000, nullptr));
837 : else
838 1 : poGeom = oChildren.clone();
839 :
840 1 : if (poGeom != nullptr)
841 1 : poFeature->SetGeometryDirectly(poGeom);
842 :
843 1 : ConsiderBrush(psElement, szPen, poFeature);
844 : }
845 1 : break;
846 :
847 242 : default:
848 242 : break;
849 : }
850 :
851 : /* -------------------------------------------------------------------- */
852 : /* Fixup geometry dimension. */
853 : /* -------------------------------------------------------------------- */
854 284 : if (poFeature->GetGeometryRef() != nullptr)
855 84 : poFeature->GetGeometryRef()->setCoordinateDimension(
856 42 : DGNGetDimension(hDGN));
857 :
858 568 : return poFeature;
859 : }
860 :
861 : /************************************************************************/
862 : /* GetNextFeature() */
863 : /************************************************************************/
864 :
865 37 : OGRFeature *OGRDGNLayer::GetNextFeature()
866 :
867 : {
868 37 : DGNGetElementIndex(hDGN, nullptr);
869 :
870 37 : DGNElemCore *psElement = nullptr;
871 285 : while ((psElement = DGNReadElement(hDGN)) != nullptr)
872 : {
873 282 : if (psElement->deleted)
874 : {
875 0 : DGNFreeElement(hDGN, psElement);
876 0 : continue;
877 : }
878 :
879 282 : OGRFeature *poFeature = ElementToFeature(psElement, 0);
880 282 : DGNFreeElement(hDGN, psElement);
881 :
882 282 : if (poFeature == nullptr)
883 0 : continue;
884 :
885 282 : if (poFeature->GetGeometryRef() == nullptr)
886 : {
887 242 : delete poFeature;
888 242 : continue;
889 : }
890 :
891 74 : if ((m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)) &&
892 34 : FilterGeometry(poFeature->GetGeometryRef()))
893 34 : return poFeature;
894 :
895 6 : delete poFeature;
896 : }
897 :
898 3 : return nullptr;
899 : }
900 :
901 : /************************************************************************/
902 : /* TestCapability() */
903 : /************************************************************************/
904 :
905 72 : int OGRDGNLayer::TestCapability(const char *pszCap)
906 :
907 : {
908 72 : if (EQUAL(pszCap, OLCRandomRead))
909 0 : return TRUE;
910 :
911 72 : else if (EQUAL(pszCap, OLCSequentialWrite))
912 16 : return bUpdate;
913 56 : else if (EQUAL(pszCap, OLCRandomWrite))
914 0 : return FALSE; /* maybe later? */
915 :
916 56 : else if (EQUAL(pszCap, OLCFastFeatureCount))
917 0 : return m_poFilterGeom == nullptr || m_poAttrQuery == nullptr;
918 :
919 56 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
920 0 : return FALSE;
921 :
922 56 : else if (EQUAL(pszCap, OLCFastGetExtent))
923 0 : return TRUE;
924 :
925 56 : else if (EQUAL(pszCap, OLCZGeometries))
926 0 : return TRUE;
927 :
928 56 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
929 4 : return !m_poDS->GetEncoding().empty();
930 :
931 52 : return FALSE;
932 : }
933 :
934 : /************************************************************************/
935 : /* GetFeatureCount() */
936 : /************************************************************************/
937 :
938 0 : GIntBig OGRDGNLayer::GetFeatureCount(int bForce)
939 :
940 : {
941 : /* -------------------------------------------------------------------- */
942 : /* If any odd conditions are in effect collect the information */
943 : /* normally. */
944 : /* -------------------------------------------------------------------- */
945 0 : if (m_poFilterGeom != nullptr || m_poAttrQuery != nullptr)
946 0 : return OGRLayer::GetFeatureCount(bForce);
947 :
948 : /* -------------------------------------------------------------------- */
949 : /* Otherwise scan the index. */
950 : /* -------------------------------------------------------------------- */
951 0 : int nElementCount = 0;
952 0 : const DGNElementInfo *pasIndex = DGNGetElementIndex(hDGN, &nElementCount);
953 :
954 0 : int nFeatureCount = 0;
955 0 : bool bInComplexShape = false;
956 :
957 0 : for (int i = 0; i < nElementCount; i++)
958 : {
959 0 : if (pasIndex[i].flags & DGNEIF_DELETED)
960 0 : continue;
961 :
962 0 : switch (pasIndex[i].stype)
963 : {
964 0 : case DGNST_MULTIPOINT:
965 : case DGNST_ARC:
966 : case DGNST_TEXT:
967 0 : if (!(pasIndex[i].flags & DGNEIF_COMPLEX) || !bInComplexShape)
968 : {
969 0 : nFeatureCount++;
970 0 : bInComplexShape = false;
971 : }
972 0 : break;
973 :
974 0 : case DGNST_COMPLEX_HEADER:
975 0 : nFeatureCount++;
976 0 : bInComplexShape = true;
977 0 : break;
978 :
979 0 : default:
980 0 : break;
981 : }
982 : }
983 :
984 0 : return nFeatureCount;
985 : }
986 :
987 : /************************************************************************/
988 : /* IGetExtent() */
989 : /************************************************************************/
990 :
991 0 : OGRErr OGRDGNLayer::IGetExtent(int /* iGeomField */, OGREnvelope *psExtent,
992 : bool /* bForce */)
993 : {
994 : double adfExtents[6];
995 :
996 0 : if (!DGNGetExtents(hDGN, adfExtents))
997 0 : return OGRERR_FAILURE;
998 :
999 0 : psExtent->MinX = adfExtents[0];
1000 0 : psExtent->MinY = adfExtents[1];
1001 0 : psExtent->MaxX = adfExtents[3];
1002 0 : psExtent->MaxY = adfExtents[4];
1003 :
1004 0 : return OGRERR_NONE;
1005 : }
1006 :
1007 : /************************************************************************/
1008 : /* LineStringToElementGroup() */
1009 : /* */
1010 : /* Convert an OGR line string to one or more DGN elements. If */
1011 : /* the input is too long for a single element (more than 38 */
1012 : /* points) we split it into multiple LINE_STRING elements, and */
1013 : /* prefix with a complex group header element. */
1014 : /* */
1015 : /* This method can create handle creating shapes, or line */
1016 : /* strings for the aggregate object, but the components of a */
1017 : /* complex shape group are always line strings. */
1018 : /************************************************************************/
1019 :
1020 : constexpr int MAX_ELEM_POINTS = 38;
1021 :
1022 29 : DGNElemCore **OGRDGNLayer::LineStringToElementGroup(const OGRLineString *poLS,
1023 : int nGroupType)
1024 :
1025 : {
1026 29 : const int nTotalPoints = poLS->getNumPoints();
1027 29 : int iGeom = 0;
1028 : DGNElemCore **papsGroup = static_cast<DGNElemCore **>(
1029 29 : CPLCalloc(sizeof(void *), (nTotalPoints / (MAX_ELEM_POINTS - 1)) + 3));
1030 :
1031 59 : for (int iNextPoint = 0; iNextPoint < nTotalPoints;)
1032 : {
1033 30 : DGNPoint asPoints[MAX_ELEM_POINTS] = {};
1034 30 : int nThisCount = 0;
1035 :
1036 : // we need to repeat end points of elements.
1037 : // cppcheck-suppress duplicateExpression
1038 30 : if (iNextPoint != 0)
1039 1 : iNextPoint--;
1040 :
1041 199 : for (; iNextPoint < nTotalPoints && nThisCount < MAX_ELEM_POINTS;
1042 : iNextPoint++, nThisCount++)
1043 : {
1044 169 : asPoints[nThisCount].x = poLS->getX(iNextPoint);
1045 169 : asPoints[nThisCount].y = poLS->getY(iNextPoint);
1046 169 : asPoints[nThisCount].z = poLS->getZ(iNextPoint);
1047 : }
1048 :
1049 30 : if (nTotalPoints <= MAX_ELEM_POINTS)
1050 28 : papsGroup[0] =
1051 28 : DGNCreateMultiPointElem(hDGN, nGroupType, nThisCount, asPoints);
1052 : else
1053 2 : papsGroup[++iGeom] = DGNCreateMultiPointElem(hDGN, DGNT_LINE_STRING,
1054 : nThisCount, asPoints);
1055 : }
1056 :
1057 : /* -------------------------------------------------------------------- */
1058 : /* We needed to make into a group. Create the complex header */
1059 : /* from the rest of the group. */
1060 : /* -------------------------------------------------------------------- */
1061 29 : if (papsGroup[0] == nullptr)
1062 : {
1063 1 : if (nGroupType == DGNT_SHAPE)
1064 0 : nGroupType = DGNT_COMPLEX_SHAPE_HEADER;
1065 : else
1066 1 : nGroupType = DGNT_COMPLEX_CHAIN_HEADER;
1067 :
1068 1 : papsGroup[0] = DGNCreateComplexHeaderFromGroup(hDGN, nGroupType, iGeom,
1069 : papsGroup + 1);
1070 : }
1071 :
1072 29 : return papsGroup;
1073 : }
1074 :
1075 : /************************************************************************/
1076 : /* TranslateLabel() */
1077 : /* */
1078 : /* Translate LABEL feature. */
1079 : /************************************************************************/
1080 :
1081 2 : DGNElemCore **OGRDGNLayer::TranslateLabel(OGRFeature *poFeature)
1082 :
1083 : {
1084 2 : OGRPoint *poPoint = poFeature->GetGeometryRef()->toPoint();
1085 2 : const char *pszText = poFeature->GetFieldAsString("Text");
1086 :
1087 4 : OGRStyleMgr oMgr;
1088 2 : oMgr.InitFromFeature(poFeature);
1089 2 : OGRStyleLabel *poLabel = reinterpret_cast<OGRStyleLabel *>(oMgr.GetPart(0));
1090 2 : if (poLabel != nullptr && poLabel->GetType() != OGRSTCLabel)
1091 : {
1092 0 : delete poLabel;
1093 0 : poLabel = nullptr;
1094 : }
1095 :
1096 2 : double dfRotation = 0.0;
1097 2 : double dfCharHeight = 100.0;
1098 2 : int nFontID = 1; // 1 is the default font for DGN. Not 0.
1099 :
1100 2 : if (poLabel != nullptr)
1101 : {
1102 : GBool bDefault;
1103 :
1104 1 : if (poLabel->TextString(bDefault) != nullptr && !bDefault)
1105 1 : pszText = poLabel->TextString(bDefault);
1106 1 : dfRotation = poLabel->Angle(bDefault);
1107 :
1108 1 : poLabel->Size(bDefault);
1109 1 : if (!bDefault && poLabel->GetUnit() == OGRSTUGround)
1110 0 : dfCharHeight = poLabel->Size(bDefault);
1111 : // this part is really kind of bogus.
1112 1 : if (!bDefault && poLabel->GetUnit() == OGRSTUMM)
1113 1 : dfCharHeight = poLabel->Size(bDefault) / 1000.0;
1114 :
1115 : /* get font id */
1116 : static const char *const papszFontNumbers[] = {
1117 : "STANDARD=0",
1118 : "WORKING=1",
1119 : "FANCY=2",
1120 : "ENGINEERING=3",
1121 : "NEWZERO=4",
1122 : "STENCEL=5",
1123 : "USTN_FANCY=7",
1124 : "COMPRESSED=8",
1125 : "STENCEQ=9",
1126 : "hand=10",
1127 : "ARCH=11",
1128 : "ARCHB=12",
1129 : "IGES1001=15",
1130 : "IGES1002=16",
1131 : "IGES1003=17",
1132 : "CENTB=18",
1133 : "MICROS=19",
1134 : "ISOFRACTIONS=22",
1135 : "ITALICS=23",
1136 : "ISO30=24",
1137 : "GREEK=25",
1138 : "ISOREC=26",
1139 : "Isoeq=27",
1140 : "ISO_FONTLEFT=30",
1141 : "ISO_FONTRIGHT=31",
1142 : "INTL_ENGINEERING=32",
1143 : "INTL_WORKING=33",
1144 : "ISOITEQ=34",
1145 : "USTN FONT 26=36",
1146 : "ARCHITECTURAL=41",
1147 : "BLOCK_OUTLINE=42",
1148 : "LOW_RES_FILLED=43",
1149 : "UPPERCASE50",
1150 : "FONT060=60",
1151 : "din=61",
1152 : "dinit=62",
1153 : "helvl=63",
1154 : "HELVLIT=64",
1155 : "helv=65",
1156 : "HELVIT=66",
1157 : "cent=67",
1158 : "CENTIT=68",
1159 : "SCRIPT=69",
1160 : "MICROQ=76",
1161 : "dotfont=77",
1162 : "DOTIT=78",
1163 : "FONT092=92",
1164 : "FONT094=94",
1165 : "ANSI_SYMBOLS=100",
1166 : "FEATURE_CONTROL_SYSMBOLS=101",
1167 : "SYMB_FAST=102",
1168 : "INTL_ISO=105",
1169 : "INTL_ISO_EQUAL=106",
1170 : "INTL_ISO_ITALIC=107",
1171 : "INTL_ISO_ITALIC_EQUAL=108",
1172 : nullptr};
1173 :
1174 1 : const char *pszFontName = poLabel->FontName(bDefault);
1175 1 : if (!bDefault && pszFontName != nullptr)
1176 : {
1177 1 : const char *pszFontNumber = CSLFetchNameValue(
1178 : const_cast<char **>(papszFontNumbers), pszFontName);
1179 :
1180 1 : if (pszFontNumber != nullptr)
1181 : {
1182 1 : nFontID = atoi(pszFontNumber);
1183 : }
1184 : }
1185 : }
1186 :
1187 2 : std::string osText;
1188 2 : const auto &osEncoding = m_poDS->GetEncoding();
1189 2 : if (!osEncoding.empty() && osEncoding != CPL_ENC_UTF8)
1190 : {
1191 1 : osText = CPLString(pszText).Recode(CPL_ENC_UTF8, osEncoding.c_str());
1192 : }
1193 : else
1194 : {
1195 1 : osText = pszText;
1196 : }
1197 :
1198 : DGNElemCore **papsGroup =
1199 2 : static_cast<DGNElemCore **>(CPLCalloc(sizeof(void *), 2));
1200 2 : papsGroup[0] =
1201 2 : DGNCreateTextElem(hDGN, osText.c_str(), nFontID, DGNJ_LEFT_BOTTOM,
1202 : dfCharHeight, dfCharHeight, dfRotation, nullptr,
1203 : poPoint->getX(), poPoint->getY(), poPoint->getZ());
1204 :
1205 2 : if (poLabel)
1206 1 : delete poLabel;
1207 :
1208 4 : return papsGroup;
1209 : }
1210 :
1211 : /************************************************************************/
1212 : /* ICreateFeature() */
1213 : /* */
1214 : /* Create a new feature and write to file. */
1215 : /************************************************************************/
1216 :
1217 72 : OGRErr OGRDGNLayer::ICreateFeature(OGRFeature *poFeature)
1218 :
1219 : {
1220 72 : if (!bUpdate)
1221 : {
1222 0 : CPLError(CE_Failure, CPLE_AppDefined,
1223 : "Attempt to create feature on read-only DGN file.");
1224 0 : return OGRERR_FAILURE;
1225 : }
1226 :
1227 72 : return CreateFeatureWithGeom(poFeature, poFeature->GetGeometryRef());
1228 : }
1229 :
1230 : /************************************************************************/
1231 : /* CreateFeatureWithGeom() */
1232 : /* */
1233 : /* Create an element or element group from a given geometry and */
1234 : /* the given feature. This method recurses to handle */
1235 : /* collections as essentially independent features. */
1236 : /************************************************************************/
1237 :
1238 98 : OGRErr OGRDGNLayer::CreateFeatureWithGeom(OGRFeature *poFeature,
1239 : const OGRGeometry *poGeom)
1240 :
1241 : {
1242 :
1243 98 : if (poGeom == nullptr || poGeom->IsEmpty())
1244 : {
1245 36 : CPLError(CE_Failure, CPLE_AppDefined,
1246 : "Features with empty, geometry collection geometries not\n"
1247 : "supported in DGN format.");
1248 36 : return OGRERR_FAILURE;
1249 : }
1250 :
1251 : /* -------------------------------------------------------------------- */
1252 : /* Translate the geometry. */
1253 : /* -------------------------------------------------------------------- */
1254 62 : DGNElemCore **papsGroup = nullptr;
1255 62 : const char *pszStyle = poFeature->GetStyleString();
1256 :
1257 62 : if (wkbFlatten(poGeom->getGeometryType()) == wkbPoint)
1258 : {
1259 16 : const OGRPoint *poPoint = poGeom->toPoint();
1260 16 : const char *pszText = poFeature->GetFieldAsString("Text");
1261 :
1262 16 : if ((pszText == nullptr || strlen(pszText) == 0) &&
1263 0 : (pszStyle == nullptr || strstr(pszStyle, "LABEL") == nullptr))
1264 : {
1265 : // Treat a non text point as a degenerate line.
1266 14 : DGNPoint asPoints[2] = {};
1267 14 : asPoints[0].x = poPoint->getX();
1268 14 : asPoints[0].y = poPoint->getY();
1269 14 : asPoints[0].z = poPoint->getZ();
1270 14 : asPoints[1] = asPoints[0];
1271 :
1272 : papsGroup =
1273 14 : static_cast<DGNElemCore **>(CPLCalloc(sizeof(void *), 2));
1274 14 : papsGroup[0] =
1275 14 : DGNCreateMultiPointElem(hDGN, DGNT_LINE, 2, asPoints);
1276 : }
1277 : else
1278 : {
1279 2 : papsGroup = TranslateLabel(poFeature);
1280 : }
1281 : }
1282 46 : else if (wkbFlatten(poGeom->getGeometryType()) == wkbLineString)
1283 : {
1284 : papsGroup =
1285 16 : LineStringToElementGroup(poGeom->toLineString(), DGNT_LINE_STRING);
1286 : }
1287 30 : else if (wkbFlatten(poGeom->getGeometryType()) == wkbPolygon)
1288 : {
1289 13 : const OGRPolygon *poPoly = poGeom->toPolygon();
1290 :
1291 : DGNElemCore **papsGroupExt =
1292 13 : LineStringToElementGroup(poPoly->getExteriorRing(), DGNT_SHAPE);
1293 :
1294 13 : const int innerRingsCnt = poPoly->getNumInteriorRings();
1295 :
1296 13 : if (innerRingsCnt > 0)
1297 : {
1298 0 : CPLDebug("InnerRings", "there are %d inner rings", innerRingsCnt);
1299 0 : std::list<DGNElemCore *> dgnElements;
1300 :
1301 0 : for (int i = 0; papsGroupExt[i] != nullptr; i++)
1302 : {
1303 0 : dgnElements.push_back(papsGroupExt[i]);
1304 : }
1305 0 : CPLFree(papsGroupExt);
1306 :
1307 : // get all interior rings and create complex group shape
1308 0 : for (int iRing = 0; iRing < innerRingsCnt; iRing++)
1309 : {
1310 0 : DGNElemCore **papsGroupInner = LineStringToElementGroup(
1311 0 : poPoly->getInteriorRing(iRing), DGNT_SHAPE);
1312 0 : papsGroupInner[0]->properties |= DGNPF_HOLE;
1313 0 : DGNUpdateElemCoreExtended(hDGN, papsGroupInner[0]);
1314 0 : for (int i = 0; papsGroupInner[i] != nullptr; i++)
1315 : {
1316 0 : dgnElements.push_back(papsGroupInner[i]);
1317 : }
1318 0 : CPLFree(papsGroupInner);
1319 : }
1320 0 : int index = 1;
1321 0 : papsGroup = (DGNElemCore **)CPLCalloc(sizeof(void *),
1322 0 : dgnElements.size() + 2);
1323 0 : for (std::list<DGNElemCore *>::iterator list_iter =
1324 0 : dgnElements.begin();
1325 0 : list_iter != dgnElements.end(); ++list_iter)
1326 : {
1327 0 : papsGroup[index++] = *list_iter;
1328 : }
1329 :
1330 : // papsGroup[0] = DGNCreateComplexHeaderFromGroup(
1331 : // hDGN, DGNT_COMPLEX_SHAPE_HEADER, dgnElements.size(),
1332 : // papsGroup+1 );
1333 0 : DGNPoint asPoints[1] = {};
1334 0 : papsGroup[0] = DGNCreateCellHeaderFromGroup(
1335 0 : hDGN, "", 1, nullptr, static_cast<int>(dgnElements.size()),
1336 : papsGroup + 1, asPoints + 0, 1.0, 1.0, 0.0);
1337 0 : DGNAddShapeFillInfo(hDGN, papsGroup[0], 6);
1338 : }
1339 : else
1340 : {
1341 13 : papsGroup = papsGroupExt;
1342 : }
1343 : }
1344 17 : else if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiPolygon ||
1345 13 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint ||
1346 35 : wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString ||
1347 5 : wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection)
1348 : {
1349 42 : for (auto &&poMember : poGeom->toGeometryCollection())
1350 : {
1351 26 : OGRErr eErr = CreateFeatureWithGeom(poFeature, poMember);
1352 26 : if (eErr != OGRERR_NONE)
1353 1 : return eErr;
1354 : }
1355 :
1356 16 : return OGRERR_NONE;
1357 : }
1358 : else
1359 : {
1360 0 : CPLError(CE_Failure, CPLE_AppDefined,
1361 : "Unsupported geometry type (%s) for DGN.",
1362 0 : OGRGeometryTypeToName(poGeom->getGeometryType()));
1363 0 : return OGRERR_FAILURE;
1364 : }
1365 :
1366 : /* -------------------------------------------------------------------- */
1367 : /* Add other attributes. */
1368 : /* -------------------------------------------------------------------- */
1369 45 : int nLevel = poFeature->GetFieldAsInteger("Level");
1370 45 : int nGraphicGroup = poFeature->GetFieldAsInteger("GraphicGroup");
1371 45 : int nColor = poFeature->GetFieldAsInteger("ColorIndex");
1372 45 : int nWeight = poFeature->GetFieldAsInteger("Weight");
1373 45 : int nStyle = poFeature->GetFieldAsInteger("Style");
1374 45 : int nMSLink = poFeature->GetFieldAsInteger("MSLink");
1375 :
1376 : // TODO: Use std::max and std::min.
1377 45 : nLevel = std::max(0, std::min(63, nLevel));
1378 45 : nColor = std::max(0, std::min(255, nColor));
1379 45 : nWeight = std::max(0, std::min(31, nWeight));
1380 45 : nStyle = std::max(0, std::min(7, nStyle));
1381 45 : nMSLink = std::max(0, nMSLink);
1382 :
1383 45 : DGNUpdateElemCore(hDGN, papsGroup[0], nLevel, nGraphicGroup, nColor,
1384 : nWeight, nStyle);
1385 45 : DGNAddMSLink(hDGN, papsGroup[0], DGNLT_ODBC, 0, nMSLink);
1386 : /* -------------------------------------------------------------------- */
1387 : /* Write to file. */
1388 : /* -------------------------------------------------------------------- */
1389 92 : for (int i = 0; papsGroup[i] != nullptr; i++)
1390 : {
1391 47 : DGNWriteElement(hDGN, papsGroup[i]);
1392 :
1393 47 : if (i == 0)
1394 45 : poFeature->SetFID(papsGroup[i]->element_id);
1395 :
1396 47 : DGNFreeElement(hDGN, papsGroup[i]);
1397 : }
1398 :
1399 45 : CPLFree(papsGroup);
1400 :
1401 45 : return OGRERR_NONE;
1402 : }
1403 :
1404 : /************************************************************************/
1405 : /* GetDataset() */
1406 : /************************************************************************/
1407 :
1408 18 : GDALDataset *OGRDGNLayer::GetDataset()
1409 : {
1410 18 : return m_poDS;
1411 : }
|