/*************************************************************** * * MODULE: v.out.postgis * * AUTHOR(S): Martin Landa * * PURPOSE: Converts GRASS vector map layer to PostGIS * * COPYRIGHT: (C) 2012-2013 by Martin Landa, and the GRASS Development Team * * This program is free software under the GNU General * Public License (>=v2). Read the file COPYING that * comes with GRASS for details. * **************************************************************/ #include #include #include #include #include "local_proto.h" static void link_handler(void *); static void output_handler(void *); int main(int argc, char *argv[]) { struct GModule *module; struct params params; struct flags flags; int ret, field, otype, verbose; char *schema, *olayer, *pg_file; char *fid_column, *geom_column; struct Map_info In, Out; G_gisinit(argv[0]); module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("export")); G_add_keyword(_("output")); G_add_keyword(_("PostGIS")); G_add_keyword(_("simple features")); G_add_keyword(_("topology")); G_add_keyword(_("3D")); module->description = _("Exports a vector map layer to PostGIS feature table."); module->overwrite = TRUE; define_options(¶ms, &flags); if (G_parser(argc, argv)) exit(EXIT_FAILURE); /* parse parameters */ otype = Vect_option_to_types(params.type); /* if olayer not given, use input as the name */ schema = NULL; if (!params.olayer->answer) { char name[GNAME_MAX], mapset[GMAPSET_MAX]; /* check for fully qualified name */ if (G_name_is_fully_qualified(params.input->answer, name, mapset)) olayer = G_store(name); else olayer = G_store(params.input->answer); G_debug(1, "olayer=%s", olayer); } else { /* check for schema */ char **tokens; tokens = G_tokenize(params.olayer->answer, "."); if (G_number_of_tokens(tokens) == 2) { schema = G_store(tokens[0]); olayer = G_store(tokens[1]); } else { olayer = G_store(params.olayer->answer); } G_free_tokens(tokens); } /* if schema not defined, use 'public' */ if (!schema) schema = "public"; G_debug(1, "Database schema: %s", schema); /* open input for reading */ ret = Vect_open_old2(&In, params.input->answer, "", params.layer->answer); if (ret == -1) G_fatal_error(_("Unable to open vector map <%s>"), params.input->answer); if (Vect_maptype(&In) != GV_FORMAT_NATIVE) G_fatal_error(_("Vector map <%s> is not in native format. Export cancelled."), Vect_get_full_name(&In)); Vect_set_error_handler_io(&In, NULL); if (params.olink->answer) G_add_error_handler(link_handler, params.olink->answer); if (ret < 2) G_warning(_("Unable to open vector map <%s> on topological level"), params.input->answer); /* default columns */ fid_column = GV_PG_FID_COLUMN; geom_column = GV_PG_GEOMETRY_COLUMN; /* create output for writing */ pg_file = create_pgfile(params.dsn->answer, schema, params.olink->answer, params.opts->answers, flags.topo->answer ? TRUE : FALSE, &fid_column, &geom_column); G_debug(1, "fid_column: %s", fid_column); G_debug(1, "geom_column: %s", geom_column); if (!flags.table->answer) { /* check fid column */ check_columns(&In, params.layer->answer, fid_column, geom_column); } /* don't use temporary maps, writes vector features immediately to the output PostGIS layer */ putenv("GRASS_VECTOR_EXTERNAL_IMMEDIATE=1"); if (-1 == Vect_open_new(&Out, olayer, !flags.force2d->answer ? Vect_is_3d(&In) : WITHOUT_Z)) G_fatal_error(_("Unable to create PostGIS layer <%s>"), olayer); G_add_error_handler(output_handler, &Out); /* copy attributes (must be done before checking output type otherwise attributes are not copied) */ field = Vect_get_field_number(&In, params.layer->answer); /* BUG: this works only if the input vector uses for its attributes * the same PG connection to be used for the output */ if (!flags.table->answer) Vect_copy_map_dblinks(&In, &Out, TRUE); /* check output type */ if (otype < 1 && Vect_level(&In) > 1) { /* type 'auto' -> try to guess output feature type on level 2 */ if (Vect_get_num_areas(&In) > 0) otype = GV_AREA; else if (Vect_get_num_primitives(&In, GV_LINE) > 0) otype = GV_LINE; else if (Vect_get_num_primitives(&In, GV_POINT) > 0) otype = GV_POINT; } if (otype > 0) { if (otype & (GV_FACE | GV_KERNEL)) G_fatal_error(_("Feature type '%s' not supported"), params.type->answer); /* set up output feature type if possible */ if (Vect_write_line(&Out, otype, NULL, NULL) < 0) G_fatal_error(_("Feature type %d is not supported"), otype); Vect_set_constraint_type(&In, otype); } /* copy vector features & create PostGIS table */ if (Vect_copy_map_lines_field(&In, field, &Out) != 0) G_fatal_error(_("Copying features failed")); /* close input map */ Vect_close(&In); /* build topology for output map -> write output to DB */ G_message(_("Writing output...")); verbose = G_verbose(); if (!flags.topo->answer) G_set_verbose(0); /* do not print build info when writing simple features */ Vect_build_partial(&Out, GV_BUILD_NONE); if (Vect_build(&Out) != 1) G_fatal_error(_("Building %s topology failed"), flags.topo->answer ? "PostGIS" : "pseudo"); G_set_verbose(verbose); if (Vect_get_num_lines(&Out) < 1) G_fatal_error(_("No features exported. PostGIS layer <%s> not created."), Vect_get_name(&Out)); if (!flags.topo->answer) G_done_msg(n_("%d feature (%s type) written to <%s>.", "%d features (%s type) written to <%s>.", Vect_sfa_get_num_features(&Out)), Vect_sfa_get_num_features(&Out), Vect_get_finfo_geometry_type(&Out), Vect_get_name(&Out)); else G_done_msg(n_("%d primitive written to <%s>.", "%d primitives written to <%s>.", Vect_get_num_lines(&Out)), Vect_get_num_lines(&Out), Vect_get_name(&Out)); /* close output map */ Vect_close(&Out); /* remove PG file */ G_remove("", pg_file); exit(EXIT_SUCCESS); } void link_handler(void *p) { const char *link = (const char *) p; G_debug(1, "link_handler: %s", link); if (G_find_vector2(link, G_mapset())) Vect_delete(link); } void output_handler(void *p) { char stmt[DB_SQL_MAX]; struct Map_info *Map; struct Format_info_pg *pg_info; PGresult *result; Map = (struct Map_info *) p; pg_info = &Map->fInfo.pg; G_debug(1, "output_handler(): schema = %s; olayer = %s", pg_info->schema_name, pg_info->table_name); sprintf(stmt, "SELECT DropGeometryTable('%s', '%s')", pg_info->schema_name, pg_info->table_name); result = PQexec(pg_info->conn, stmt); /* be quiet - table may do not exists if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) { G_warning(_("Unable to drop table <%s.%s>"), pg_info->schema_name, pg_info->table_name); } */ PQclear(result); if (pg_info->toposchema_name) { sprintf(stmt, "SELECT topology.DropTopology('%s')", pg_info->toposchema_name); result = PQexec(pg_info->conn, stmt); if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) { G_warning(_("Unable to drop topology schema <%s>"), pg_info->toposchema_name); } PQclear(result); } }