/*
This file is part of extract-polygons.
extract-polygons is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
extract-polygons is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with extract-polygons. If not, see .
*/
#include
#include
#include
#include
#include
#include "hashtable.h"
#include "table.h"
#include "polygon.h"
#include "extract-polygon.h"
#ifdef LIBXML_READER_ENABLED
#define OSM_VERSION "0.6"
void die(char *message) {
fprintf(stderr, "%s\n", message);
exit(-1);
}
int bz2InputReadCallback(void *context, char *buffer, int len) {
int bzerr;
return BZ2_bzRead (&bzerr, (BZFILE *)context, buffer, len);
}
int bz2InputCloseCallback(void *context) {
int bzerr;
BZ2_bzReadClose(&bzerr, (BZFILE *)context);
return bzerr == BZ_OK;
}
xmlTextReaderPtr myXmlReaderForFile(const char *filename) {
xmlTextReaderPtr reader = NULL;
// Try BZ2
size_t len = strlen(filename);
if (len > 4) {
if (strcmp(filename+len-4, ".bz2") == 0) {
int nUnused = 0;
char unused[BZ_MAX_UNUSED];
int bzerr;
FILE *f = fopen(filename, "r");
BZFILE* bzf = BZ2_bzReadOpen(&bzerr, f, 0, 0, unused, nUnused);
if (bzf == NULL || bzerr != BZ_OK) {
die("Failed to uncompress bz2 file");
}
reader = xmlReaderForIO(bz2InputReadCallback, bz2InputCloseCallback, bzf, "", NULL, 0);
}
}
// Try normal file
if (reader == NULL) {
reader = xmlReaderForFile(filename, NULL, 0);
}
if (reader == NULL) {
die("Unable to open XML file");
}
return reader;
}
void copyAttributes(xmlTextReaderPtr reader, xmlTextWriterPtr writer) {
const xmlChar *name, *value;
xmlTextReaderMoveToFirstAttribute(reader);
do {
name = xmlTextReaderConstName(reader);
value = xmlTextReaderConstValue(reader);
xmlTextWriterWriteAttribute(writer, name, value);
} while (xmlTextReaderMoveToNextAttribute(reader));
xmlTextReaderMoveToElement(reader);
}
static void cutFile(const char *inFilename, const char *outFilename, struct hashtable *includeNodes, struct hashtable *includeWays, struct hashtable *includeRelations) {
xmlTextReaderPtr reader;
xmlTextWriterPtr writer;
int ret, rc;
reader = myXmlReaderForFile(inFilename);
writer = xmlNewTextWriterFilename(outFilename, 0);
if (writer == NULL) {
die("Error creating XML writer");
}
xmlTextWriterSetIndent(writer, 1);
rc = xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL);
if (rc < 0) {
die("Error starting XML document");
}
rc = xmlTextWriterStartElement(writer, BAD_CAST "osm");
if (rc < 0) {
die("Error creating XML element");
}
rc = xmlTextWriterWriteAttribute(writer, BAD_CAST "version",
BAD_CAST OSM_VERSION);
if (rc < 0) {
die("Error creating XML attribute");
}
rc = xmlTextWriterWriteAttribute(writer, BAD_CAST "generator",
BAD_CAST "extract-polygons.c");
if (rc < 0) {
die("Error creating XML attribute");
}
const xmlChar *name;
int copy = 1;
do {
ret = xmlTextReaderRead(reader);
if (ret != 1) {
break;
}
name = xmlTextReaderConstName(reader);
if (copy && xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT) {
rc = xmlTextWriterEndElement(writer);
if (rc < 0) {
die("Error creating XML element");
}
continue;
}
if (strcmp((char *)name, "node") == 0) {
xmlChar *att = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
if (table_get(includeNodes, atoi((char *)att))) {
copy = 1;
rc = xmlTextWriterStartElement(writer, BAD_CAST "node");
if (rc < 0) {
die("Error creating XML element");
}
copyAttributes(reader, writer);
} else {
copy = 0;
}
free(att);
} else if (strcmp((char *)name, "way") == 0) {
xmlChar *att = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
if (table_get(includeWays, atoi((char *)att))) {
copy = 1;
rc = xmlTextWriterStartElement(writer, BAD_CAST "way");
if (rc < 0) {
die("Error creating XML element");
}
copyAttributes(reader, writer);
} else {
copy = 0;
}
free(att);
} else if (strcmp((char *)name, "relation") == 0) {
xmlChar *att = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
if (table_get(includeRelations, atoi((char *)att))) {
copy = 1;
rc = xmlTextWriterStartElement(writer, BAD_CAST "relation");
if (rc < 0) {
die("Error creating XML element");
}
copyAttributes(reader, writer);
} else {
copy = 0;
}
free(att);
} else if (strcmp((char *)name, "bound") == 0) {
rc = xmlTextWriterStartElement(writer, BAD_CAST "bound");
if (rc < 0) {
die("Error creating XML element");
}
copyAttributes(reader, writer);
} else if (copy) {
if (strcmp((char *)name, "nd") == 0 ||
strcmp((char *)name, "tag") == 0 ||
strcmp((char *)name, "member") == 0) {
rc = xmlTextWriterStartElement(writer, BAD_CAST xmlTextReaderConstName(reader));
if (rc < 0) {
die("Error creating XML element");
}
copyAttributes(reader, writer);
rc = xmlTextWriterEndElement(writer);
if (rc < 0) {
die("Error ending XML element");
}
continue;
} else if (xmlTextReaderIsEmptyElement(reader)) {
rc = xmlTextWriterEndElement(writer);
if (rc < 0) {
die("Error ending XML element");
}
continue;
}
}
if (copy) {
if (xmlTextReaderIsEmptyElement(reader)) {
rc = xmlTextWriterEndElement(writer);
if (rc < 0) {
die("Error ending XML element");
}
}
}
} while (ret == 1);
rc = xmlTextWriterEndDocument(writer);
if (rc < 0) {
die("Error ending XML document");
}
xmlFreeTextReader(reader);
xmlFreeTextWriter(writer);
if (ret != 0) {
die("XML parse error");
}
}
static void fillFromUp(struct hashtable *down, struct hashtable *up, int *nodes, int nodesPos, int id) {
for (int i = 0 ; i < nodesPos ; i++) {
if (table_get(down, nodes[i])) {
table_set(up, id);
for (int j = 0 ; j < nodesPos ; j++) {
table_set(down, nodes[j]);
}
break;
}
}
}
static int fillIncludes(const char *inFilename, struct hashtable *includeNodes, struct hashtable *includeWays, struct hashtable *includeRelations, Tpolygon *polygon) {
xmlTextReaderPtr reader;
int ret;
int restart = 0;
int status = 0;
reader = myXmlReaderForFile(inFilename);
const xmlChar *name;
int type;
int* nodes;
int nodesPos = 0;
int nodesSize = 64;
nodes = malloc(nodesSize * sizeof(int));
if (nodes == NULL) {
die("Can't allocated memory!");
}
do {
ret = xmlTextReaderRead(reader);
if (ret != 1) {
break;
}
type = xmlTextReaderNodeType(reader);
if (type != XML_READER_TYPE_ELEMENT && type != XML_READER_TYPE_END_ELEMENT) {
continue;
}
name = xmlTextReaderConstName(reader);
if (strcmp((char *)name, "tag") == 0) {
} else if (strcmp((char *)name, "node") == 0) {
if (status > 1) {
restart = 1;
printf("Non linear OSM file, two phase reading\n");
} else if (status != 1) {
status = 1;
printf("Loading nodes...\n");
}
if (type == XML_READER_TYPE_ELEMENT) {
xmlChar *att;
att = xmlTextReaderGetAttribute(reader, (xmlChar *)"lat");
float lat = atof((char *)att);
free(att);
att = xmlTextReaderGetAttribute(reader, (xmlChar *)"lon");
float lon = atof((char *)att);
free(att);
if (polygon_pointInside(polygon, lat, lon)) {
att = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
table_set(includeNodes, atoi((char *)att));
free(att);
}
}
} else if (strcmp((char *)name, "nd") == 0) {
if (type == XML_READER_TYPE_ELEMENT) {
xmlChar *att = xmlTextReaderGetAttribute(reader, (xmlChar *)"ref");
nodes[nodesPos++] = atoi((char *)att);
free(att);
if (nodesPos == nodesSize) {
nodesSize *= 2;
nodes = realloc(nodes, nodesSize * sizeof(int));
if (nodes == NULL) {
die("Can't allocated memory!");
}
}
}
} else if (strcmp((char *)name, "way") == 0) {
if (status > 2) {
restart = 1;
printf("Non linear OSM file, two phase reading\n");
} else if (status != 2) {
status = 2;
printf("Loading ways...\n");
}
if (type == XML_READER_TYPE_ELEMENT) {
nodesPos = 0;
} else {
xmlChar *att = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
fillFromUp(includeNodes, includeWays, nodes, nodesPos, atoi((char *)att));
free(att);
}
} else if (strcmp((char *)name, "relation") == 0) {
if (status > 3) {
restart = 1;
printf("Non linear OSM file, two phase reading\n");
} else if (status != 3) {
status = 3;
printf("Loading relations...\n");
}
if (type == XML_READER_TYPE_ELEMENT) {
nodesPos = 0;
} else {
xmlChar *att = xmlTextReaderGetAttribute(reader, (xmlChar *)"id");
fillFromUp(includeWays, includeRelations, nodes, nodesPos, atoi((char *)att));
free(att);
}
} else if (strcmp((char *)name, "member") == 0) {
if (type == XML_READER_TYPE_ELEMENT) {
xmlChar *att = xmlTextReaderGetAttribute(reader, (xmlChar *)"ref");
nodes[nodesPos++] = atoi((char *)att);
free(att);
if (nodesPos == nodesSize) {
nodesSize *= 2;
nodes = realloc(nodes, nodesSize * sizeof(int));
if (nodes == NULL) {
die("Can't allocated memory!");
}
}
}
} else if (strcmp((char *)name, "bound") == 0) {
} else if (strcmp((char *)name, "osm") == 0) {
xmlChar *att = xmlTextReaderGetAttribute(reader, (xmlChar *)"version");
if (strcmp((char *)att, OSM_VERSION) != 0) {
free(att);
die("OSM version mismatch");
}
free(att);
} else {
die("Unknown XML element name");
}
} while (ret == 1);
xmlFreeTextReader(reader);
if (ret != 0) {
die("XML parse error");
}
return restart;
}
int main(int argc, char **argv) {
if (argc != 4) {
printf("Usage: extract-polygon \n");
return(1);
}
LIBXML_TEST_VERSION
struct hashtable *includeNodes;
struct hashtable *includeWays;
struct hashtable *includeRelations;
Tpolygon polygon;
includeNodes = table_init();
includeWays = table_init();
includeRelations = table_init();
printf("Loading polygon...\n");
polygon_load(&polygon, argv[3]);
if (fillIncludes(argv[1], includeNodes, includeWays, includeRelations, &polygon)) {
// Non linear OSM file, do it again
fillIncludes(argv[1], includeNodes, includeWays, includeRelations, &polygon);
}
printf("Cutting file...\n");
cutFile(argv[1], argv[2], includeNodes, includeWays, includeRelations);
table_destruct(includeNodes);
table_destruct(includeWays);
table_destruct(includeRelations);
xmlCleanupParser();
xmlMemoryDump();
return(0);
}
#else
int main(void) {
fprintf(stderr, "XInclude support not compiled in\n");
exit(1);
}
#endif