Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2005-2025 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file polyconvert_main.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Christoph Sommer
18 : /// @author Michael Behrisch
19 : /// @author Melanie Knocke
20 : /// @date Mon, 05 Dec 2005
21 : ///
22 : // Main for POLYCONVERT
23 : /****************************************************************************/
24 : #include <config.h>
25 :
26 : #ifdef HAVE_VERSION_H
27 : #include <version.h>
28 : #endif
29 :
30 : #include <iostream>
31 : #include <string>
32 : #include <utils/options/OptionsIO.h>
33 : #include <utils/options/OptionsCont.h>
34 : #include <utils/common/UtilExceptions.h>
35 : #include <utils/common/StringTokenizer.h>
36 : #include <utils/common/SystemFrame.h>
37 : #include <utils/common/MsgHandler.h>
38 : #include <utils/common/StringUtils.h>
39 : #include <utils/common/ToString.h>
40 : #include <utils/iodevices/OutputDevice.h>
41 : #include <utils/importio/LineReader.h>
42 : #include <utils/geom/GeoConvHelper.h>
43 : #include <utils/geom/GeomConvHelper.h>
44 : #include <utils/geom/Boundary.h>
45 : #include <utils/xml/SUMOSAXReader.h>
46 : #include <utils/xml/XMLSubSys.h>
47 : #include <polyconvert/PCLoaderVisum.h>
48 : #include <polyconvert/PCLoaderDlrNavteq.h>
49 : #include <polyconvert/PCLoaderXML.h>
50 : #include <polyconvert/PCLoaderOSM.h>
51 : #include <polyconvert/PCLoaderArcView.h>
52 : #include <polyconvert/PCTypeMap.h>
53 : #include <polyconvert/PCTypeDefHandler.h>
54 : #include <polyconvert/PCNetProjectionLoader.h>
55 : #include <polyconvert/pc_typemap.h>
56 :
57 :
58 : // ===========================================================================
59 : // method definitions
60 : // ===========================================================================
61 : void
62 52 : fillOptions() {
63 52 : OptionsCont& oc = OptionsCont::getOptions();
64 104 : oc.addCallExample("-c <CONFIGURATION>", TL("run with configuration options set in file"));
65 :
66 : // insert options sub-topics
67 52 : SystemFrame::addConfigurationOptions(oc); // fill this subtopic, too
68 52 : oc.addOptionSubTopic("Input");
69 52 : oc.addOptionSubTopic("Output");
70 52 : GeoConvHelper::addProjectionOptions(oc);
71 52 : oc.addOptionSubTopic("Pruning");
72 52 : oc.addOptionSubTopic("Processing");
73 52 : oc.addOptionSubTopic("Building Defaults");
74 :
75 :
76 : // register options
77 : // add i/o options
78 : // original network
79 52 : oc.doRegister("net-file", 'n', new Option_FileName());
80 104 : oc.addSynonyme("net-file", "net");
81 104 : oc.addDescription("net-file", "Input", TL("Loads SUMO-network FILE as reference to offset and projection"));
82 :
83 : // dlrnavteq import
84 52 : oc.doRegister("dlr-navteq-poly-files", new Option_FileName());
85 104 : oc.addDescription("dlr-navteq-poly-files", "Input", TL("Reads polygons from FILE assuming they're coded in DLR-Navteq (Elmar)-format"));
86 52 : oc.doRegister("dlr-navteq-poi-files", new Option_FileName());
87 104 : oc.addDescription("dlr-navteq-poi-files", "Input", TL("Reads pois from FILE assuming they're coded in DLR-Navteq (Elmar)-format"));
88 :
89 : // visum import
90 52 : oc.doRegister("visum-files", new Option_FileName());
91 104 : oc.addSynonyme("visum-files", "visum");
92 104 : oc.addDescription("visum-files", "Input", TL("Reads polygons from FILE assuming it's a Visum-net"));
93 :
94 52 : oc.doRegister("visum.language-file", new Option_FileName());
95 104 : oc.addDescription("visum.language-file", "Input", TL("Load language mappings from FILE"));
96 :
97 : // xml import
98 52 : oc.doRegister("xml-files", new Option_FileName());
99 104 : oc.addSynonyme("xml-files", "xml");
100 104 : oc.addDescription("xml-files", "Input", TL("Reads pois and shapes from FILE assuming they're coded in XML"));
101 :
102 : // osm import
103 52 : oc.doRegister("osm-files", new Option_FileName());
104 104 : oc.addSynonyme("osm-files", "osm");
105 104 : oc.addDescription("osm-files", "Input", TL("Reads pois from FILE assuming they're coded in OSM"));
106 52 : oc.doRegister("osm.keep-full-type", new Option_Bool(false));
107 104 : oc.addDescription("osm.keep-full-type", "Input", TL("The type will be made of the key-value - pair"));
108 52 : oc.doRegister("osm.use-name", new Option_Bool(false));
109 104 : oc.addDescription("osm.use-name", "Input", TL("The id will be set from the given 'name' attribute"));
110 52 : oc.doRegister("osm.merge-relations", new Option_Float(-1));
111 104 : oc.addDescription("osm.merge-relations", "Input", TL("If FLOAT >= 0, assemble one polygon from all ways of a relation if they all connect with gaps below FLOAT"));
112 :
113 : // geojson import
114 52 : oc.doRegister("geojson-files", new Option_FileName());
115 104 : oc.addDescription("geojson-files", "Input", TL("Reads shapes from geojson FILE"));
116 :
117 : // arcview import
118 52 : oc.doRegister("shapefile-prefixes", new Option_FileName());
119 104 : oc.addSynonyme("shapefile-prefixes", "shapefile-prefix");
120 104 : oc.addSynonyme("shapefile-prefixes", "shapefile");
121 104 : oc.addSynonyme("shapefile-prefixes", "shape-files", true);
122 104 : oc.addDescription("shapefile-prefixes", "Input", TL("Reads shapes from shapefiles FILE"));
123 :
124 52 : oc.doRegister("shapefile.guess-projection", new Option_Bool(false));
125 104 : oc.addSynonyme("shapefile.guess-projection", "arcview.guess-projection", true);
126 104 : oc.addDescription("shapefile.guess-projection", "Input", TL("Guesses the shapefile's projection"));
127 :
128 52 : oc.doRegister("shapefile.traditional-axis-mapping", new Option_Bool(false));
129 104 : oc.addDescription("shapefile.traditional-axis-mapping", "Input", TL("Use traditional axis order (lon, lat)"));
130 :
131 52 : oc.doRegister("shapefile.id-column", new Option_String());
132 104 : oc.addSynonyme("shapefile.id-column", "shapefile.id-name", true);
133 104 : oc.addSynonyme("shapefile.id-column", "shape-files.id-name", true);
134 104 : oc.addDescription("shapefile.id-column", "Input", TL("Defines in which column the id can be found"));
135 :
136 52 : oc.doRegister("shapefile.type-columns", new Option_StringVector());
137 104 : oc.addSynonyme("shapefile.type-columns", "shapefile.type-column");
138 104 : oc.addDescription("shapefile.type-columns", "Input", TL("Defines which columns form the type id (comma separated list)"));
139 :
140 52 : oc.doRegister("shapefile.use-running-id", new Option_Bool(false));
141 104 : oc.addDescription("shapefile.use-running-id", "Input", TL("A running number will be used as id"));
142 :
143 52 : oc.doRegister("shapefile.add-param", new Option_Bool(false));
144 104 : oc.addDescription("shapefile.add-param", "Input", TL("Extract all additional columns as params"));
145 :
146 52 : oc.doRegister("shapefile.fill", new Option_String());
147 104 : oc.addDescription("shapefile.fill", "Input", TL("[auto|true|false]. Forces the 'fill' status to the given value. Default 'auto' tries to determine it from the data type"));
148 :
149 : // typemap reading
150 52 : oc.doRegister("type-file", new Option_FileName());
151 104 : oc.addSynonyme("type-file", "typemap", true);
152 104 : oc.addDescription("type-file", "Input", TL("Reads types from FILE"));
153 :
154 : // need to do this here to be able to check for network and route input options
155 52 : SystemFrame::addReportOptions(oc);
156 :
157 : // output
158 52 : oc.doRegister("output-file", 'o', new Option_FileName());
159 104 : oc.addSynonyme("output-file", "output");
160 104 : oc.addDescription("output-file", "Output", TL("Write generated polygons/pois to FILE"));
161 :
162 52 : oc.doRegister("dlr-tdp-output", new Option_FileName());
163 104 : oc.addDescription("dlr-tdp-output", "Output", TL("Write generated polygons/pois to a dlr-tdp file with the given prefix"));
164 :
165 :
166 : // pruning options
167 52 : oc.doRegister("prune.in-net", new Option_Bool(false));
168 104 : oc.addSynonyme("prune.in-net", "prune.on-net", true);
169 104 : oc.addDescription("prune.in-net", TL("Pruning"), TL("Enables pruning on net boundaries"));
170 :
171 104 : oc.doRegister("prune.in-net.offsets", new Option_String("0,0,0,0"));
172 104 : oc.addSynonyme("prune.in-net.offsets", "prune.on-net.offsets", true);
173 104 : oc.addDescription("prune.in-net.offsets", TL("Pruning"), TL("Uses FLOAT,FLOAT,FLOAT,FLOAT as offset definition added to the net boundary. Positive values grow the boundary on all sides while negative values shrink it."));
174 :
175 52 : oc.doRegister("prune.boundary", new Option_String());
176 104 : oc.addDescription("prune.boundary", TL("Pruning"), TL("Uses STR as pruning boundary"));
177 :
178 52 : oc.doRegister("prune.keep-list", new Option_String());
179 104 : oc.addSynonyme("prune.keep-list", "prune.keep");
180 104 : oc.addSynonyme("prune.keep-list", "prune.ignore", true);
181 104 : oc.addDescription("prune.keep-list", TL("Pruning"), TL("Items in STR will be kept though out of boundary"));
182 :
183 208 : oc.doRegister("prune.explicit", new Option_StringVector(StringVector({ "" })));
184 104 : oc.addSynonyme("prune.explicit", "remove");
185 104 : oc.addDescription("prune.explicit", TL("Pruning"), TL("Items with names in STR[] will be removed"));
186 :
187 :
188 52 : oc.doRegister("offset.x", new Option_Float(0));
189 104 : oc.addSynonyme("offset.x", "x-offset-to-apply", true);
190 104 : oc.addDescription("offset.x", "Processing", TL("Adds FLOAT to net x-positions"));
191 :
192 52 : oc.doRegister("offset.y", new Option_Float(0));
193 104 : oc.addSynonyme("offset.y", "y-offset-to-apply", true);
194 104 : oc.addDescription("offset.y", "Processing", TL("Adds FLOAT to net y-positions"));
195 :
196 52 : oc.doRegister("offset.z", new Option_Float(0));
197 104 : oc.addDescription("offset.z", "Processing", TL("Adds FLOAT to net z-positions"));
198 :
199 52 : oc.doRegister("all-attributes", new Option_Bool(false));
200 104 : oc.addDescription("all-attributes", "Processing", TL("Imports all attributes as key/value pairs"));
201 :
202 52 : oc.doRegister("ignore-errors", new Option_Bool(false));
203 104 : oc.addDescription("ignore-errors", "Processing", TL("Continue on broken input"));
204 :
205 52 : oc.doRegister("poi-layer-offset", new Option_Float(0));
206 104 : oc.addDescription("poi-layer-offset", "Processing", TL("Adds FLOAT to the layer value for each poi (i.e. to raise it above polygons)"));
207 :
208 52 : oc.doRegister("flatten", new Option_Bool(false));
209 104 : oc.addDescription("flatten", "Processing", TL("Remove all z-data"));
210 :
211 : // building defaults options
212 104 : oc.doRegister("color", new Option_String("0.2,0.5,1."));
213 104 : oc.addDescription("color", "Building Defaults", TL("Sets STR as default color"));
214 :
215 104 : oc.doRegister("prefix", new Option_String(""));
216 104 : oc.addDescription("prefix", "Building Defaults", TL("Sets STR as default prefix"));
217 :
218 104 : oc.doRegister("type", new Option_String("unknown"));
219 104 : oc.addDescription("type", "Building Defaults", TL("Sets STR as default type"));
220 :
221 52 : oc.doRegister("fill", new Option_Bool(true));
222 104 : oc.addDescription("fill", "Building Defaults", TL("Fills polygons by default"));
223 :
224 104 : oc.doRegister("icon", new Option_String(SUMOXMLDefinitions::POIIcons.getString(POIIcon::NONE)));
225 104 : oc.addDescription("icon", "Building Defaults", TL("Sets STR as default icon"));
226 :
227 52 : oc.doRegister("layer", new Option_Float(-1));
228 104 : oc.addDescription("layer", "Building Defaults", TL("Sets FLOAT as default layer"));
229 :
230 52 : oc.doRegister("discard", new Option_Bool(false));
231 104 : oc.addDescription("discard", "Building Defaults", TL("Sets default action to discard"));
232 :
233 : // projection
234 52 : oc.doRegister("proj.plain-geo", new Option_Bool(false));
235 104 : oc.addDescription("proj.plain-geo", "Projection", TL("Write geo coordinates in output"));
236 156 : }
237 :
238 :
239 : int
240 52 : main(int argc, char** argv) {
241 52 : OptionsCont& oc = OptionsCont::getOptions();
242 52 : oc.setApplicationDescription(TL("Importer of polygons and POIs for the microscopic, multi-modal traffic simulation SUMO."));
243 104 : oc.setApplicationName("polyconvert", "Eclipse SUMO polyconvert " VERSION_STRING);
244 : int ret = 0;
245 : try {
246 : // initialise subsystems
247 52 : XMLSubSys::init();
248 52 : fillOptions();
249 52 : OptionsIO::setArgs(argc, argv);
250 52 : OptionsIO::getOptions();
251 52 : if (oc.processMetaOptions(argc < 2)) {
252 6 : SystemFrame::close();
253 6 : return 0;
254 : }
255 46 : SystemFrame::checkOptions(oc);
256 138 : XMLSubSys::setValidation(oc.getString("xml-validation"), oc.getString("xml-validation.net"), "never");
257 92 : if (oc.isDefault("aggregate-warnings")) {
258 92 : oc.setDefault("aggregate-warnings", "5");
259 : }
260 46 : MsgHandler::initOutputOptions();
261 : // build the projection
262 46 : double scale = 1.0;
263 94 : if ((oc.isSet("dlr-navteq-poly-files") || oc.isSet("dlr-navteq-poi-files")) && oc.isDefault("proj.scale")) {
264 6 : scale = 1e-5;
265 : }
266 92 : if (!oc.isSet("net")) {
267 : // from the given options
268 : #ifdef PROJ_API_FILE
269 70 : const int numProjections = oc.getBool("simple-projection") + oc.getBool("proj.utm") + oc.getBool("proj.dhdn") + (oc.getString("proj").length() > 1);
270 70 : if ((oc.isSet("osm-files")
271 54 : || oc.isSet("dlr-navteq-poly-files")
272 52 : || oc.isSet("dlr-navteq-poi-files")
273 51 : || oc.isSet("geojson-files")
274 50 : || oc.isSet("shapefile-prefixes"))
275 43 : && numProjections == 0) {
276 : // input is lon,lat and projecting it to UTM ensures accurate handling of geometry
277 48 : oc.set("proj.utm", "true");
278 48 : if (oc.isDefault("proj.plain-geo")) {
279 : // without reference to a network, raw UTM isn't helpful so we better write the data out as lon,lat
280 46 : oc.set("proj.plain-geo", "true");
281 : }
282 : }
283 70 : if (oc.isDefault("proj.scale")) {
284 70 : oc.set("proj.scale", toString(scale, 5));
285 : }
286 : #endif
287 35 : if (!GeoConvHelper::init(oc)) {
288 0 : throw ProcessError(TL("Could not build projection!"));
289 : }
290 : } else {
291 : // from the supplied network
292 : // @todo warn about given options being ignored
293 22 : PCNetProjectionLoader::load(oc.getString("net"), scale);
294 : }
295 46 : Boundary pruningBoundary = GeoConvHelper::getFinal().getConvBoundary();
296 : // check whether the input shall be pruned
297 : bool prune = false;
298 92 : if (oc.getBool("prune.in-net")) {
299 2 : if (!oc.isSet("net")) {
300 0 : throw ProcessError(TL("In order to prune the input on the net, you have to supply a network."));
301 : }
302 1 : bool ok = true;
303 : // !!! no proper error handling
304 : Boundary offsets = GeomConvHelper::parseBoundaryReporting(
305 2 : oc.getString("prune.in-net.offsets"), "--prune.on-net.offsets", nullptr, ok, true, true);
306 1 : pruningBoundary.setOffsets(
307 1 : pruningBoundary.xmin() - offsets.xmin(),
308 1 : pruningBoundary.ymin() - offsets.ymin(),
309 1 : pruningBoundary.xmax() + offsets.xmax(),
310 1 : pruningBoundary.ymax() + offsets.ymax());
311 : prune = true;
312 : }
313 92 : if (oc.isSet("prune.boundary")) {
314 0 : bool ok = true;
315 : // !!! no proper error handling
316 0 : pruningBoundary = GeomConvHelper::parseBoundaryReporting(oc.getString("prune.boundary"), "--prune.boundary", nullptr, ok);
317 : prune = true;
318 : }
319 69 : if (oc.isSet("osm-files") && oc.isDefault("poi-layer-offset")) {
320 46 : oc.setDefault("poi-layer-offset", "5"); // sufficient when using the default typemap
321 : }
322 :
323 46 : PCPolyContainer toFill(prune, pruningBoundary, oc.getStringVector("remove"));
324 :
325 : // read in the type defaults
326 92 : if (!oc.isSet("type-file")) {
327 41 : const char* sumoPath = std::getenv("SUMO_HOME");
328 41 : if (sumoPath == nullptr) {
329 0 : WRITE_WARNING(TL("Environment variable SUMO_HOME is not set, using built in type maps."));
330 : } else {
331 41 : const std::string path = sumoPath + std::string("/data/typemap/");
332 82 : if (oc.isSet("dlr-navteq-poly-files")) {
333 8 : oc.setDefault("type-file", path + "navteqPolyconvert.typ.xml");
334 : }
335 82 : if (oc.isSet("osm-files")) {
336 36 : oc.setDefault("type-file", path + "osmPolyconvert.typ.xml");
337 : }
338 82 : if (oc.isSet("visum-files")) {
339 0 : oc.setDefault("type-file", path + "visumPolyconvert.typ.xml");
340 : }
341 : }
342 : }
343 46 : PCTypeMap tm(oc);
344 46 : PCTypeDefHandler handler(oc, tm);
345 92 : if (oc.isSet("type-file")) {
346 54 : if (!XMLSubSys::runParser(handler, oc.getString("type-file"))) {
347 : // something failed
348 0 : throw ProcessError();
349 : }
350 : } else {
351 19 : handler.setFileName("built in type map");
352 19 : SUMOSAXReader* reader = XMLSubSys::getSAXReader(handler);
353 38 : if (oc.isSet("dlr-navteq-poly-files")) {
354 0 : reader->parseString(navteqTypemap);
355 : }
356 38 : if (oc.isSet("osm-files")) {
357 0 : reader->parseString(osmTypemap);
358 : }
359 38 : if (oc.isSet("visum-files")) {
360 0 : reader->parseString(visumTypemap);
361 : }
362 19 : delete reader;
363 : }
364 : // read in the data
365 46 : PCLoaderXML::loadIfSet(oc, toFill, tm); // SUMO-XML
366 45 : PCLoaderOSM::loadIfSet(oc, toFill, tm); // OSM-XML
367 44 : PCLoaderDlrNavteq::loadIfSet(oc, toFill, tm); // Elmar-files
368 44 : PCLoaderVisum::loadIfSet(oc, toFill, tm); // VISUM
369 44 : PCLoaderArcView::loadIfSet(oc, toFill, tm); // shape-files
370 44 : GeoConvHelper::computeFinal();
371 : // error processing
372 45 : if (MsgHandler::getErrorInstance()->wasInformed() && !oc.getBool("ignore-errors")) {
373 1 : throw ProcessError();
374 : }
375 : // output
376 45 : if (!oc.isSet("output-file") && !oc.isSet("dlr-tdp-output")) {
377 0 : std::string out = "polygons.xml";
378 0 : if (oc.isSet("configuration-file")) {
379 0 : out = FileHelpers::getConfigurationRelative(oc.getString("configuration-file"), out);
380 : }
381 0 : oc.setDefault("output-file", out);
382 : }
383 86 : if (oc.isSet("output-file")) {
384 84 : toFill.save(oc.getString("output-file"), oc.getBool("proj.plain-geo"));
385 : }
386 86 : if (oc.isSet("dlr-tdp-output")) {
387 2 : toFill.saveDlrTDP(oc.getString("dlr-tdp-output"));
388 : }
389 :
390 55 : } catch (const ProcessError& e) {
391 3 : if (std::string(e.what()) != std::string("Process Error") && std::string(e.what()) != std::string("")) {
392 0 : WRITE_ERROR(e.what());
393 : }
394 3 : MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
395 : ret = 1;
396 : #ifndef _DEBUG
397 3 : } catch (const std::exception& e) {
398 0 : if (std::string(e.what()) != std::string("")) {
399 0 : WRITE_ERROR(e.what());
400 : }
401 0 : MsgHandler::getErrorInstance()->inform("Quitting (on error).", false);
402 : ret = 1;
403 0 : } catch (...) {
404 0 : MsgHandler::getErrorInstance()->inform("Quitting (on unknown error).", false);
405 : ret = 1;
406 : #endif
407 0 : }
408 46 : SystemFrame::close();
409 : // report about ending
410 : if (ret == 0) {
411 : std::cout << "Success." << std::endl;
412 : }
413 : return ret;
414 : }
415 :
416 :
417 : /****************************************************************************/
|