root/trunk/clustr/clustr.cpp

Revision 460, 7.4 kB (checked in by aaron, 1 year ago)

initial public release

Line 
1 /*
2 Clustr.  Copyright (c) 2007-2008 Yahoo! Inc.
3
4 All rights reserved.  This code is free software; you can redistribute it
5 and/or modify it under the terms of the GNU General Public License (GPL),
6 version 2 only.  This code is distributed WITHOUT ANY WARRANTY, whether
7 express or implied. See the GNU GPL for more details
8 (http://www.gnu.org/licenses/gpl.html)
9
10 AS A SPECIAL EXCEPTION, YOU HAVE PERMISSION TO LINK THIS PROGRAM WITH THE
11 CGAL LIBRARY AND DISTRIBUTE EXECUTABLES, AS LONG AS YOU FOLLOW THE REQUIREMENTS
12 OF THE GNU GPL VERSION 2 IN REGARD TO ALL OF THE SOFTWARE IN THE EXECUTABLE
13 ASIDE FROM CGAL.
14 */
15
16 #include <iostream>
17 #include <fstream>
18 #include <sstream>
19 #include <vector>
20 #include <list>
21 #include <stdexcept>
22 #include <cmath>
23
24 #include <unistd.h>
25 #include "clustr.h"
26 #include "shapefile.h"
27
28 using namespace std;
29 using namespace Clustr;
30
31 void write_points_to_shapefile (Shapefile &shape, vector<Point> &pts,
32                                 string text) {
33     vector<Point>::iterator begin = pts.begin(),
34                             end   = pts.end();
35     for (vector<Point>::iterator it = begin; it != end; it++) {
36         Feature pt_feat(shape);
37         Geometry pt_geom(wkbPoint);
38         pt_geom.push_back(*it);
39         pt_feat.set(pt_geom);
40         pt_feat.set("tag", text.c_str());
41         shape.add_feature(pt_feat);
42     }
43 }
44
45 void write_polygon_to_shapefile (Shapefile &shape, Polygon &poly, string text, long int count) {
46     Feature feat(shape);
47     Geometry geom(wkbPolygon);
48     geom.insert_rings(poly.begin(), poly.end());
49     feat.set(geom);
50     feat.set("tag", text.c_str());
51     feat.set("count", count);
52     feat.set("area", poly.area());
53     feat.set("perimeter", poly.perimeter());
54     feat.set("density", ((float)count)/poly.area());
55     shape.add_feature(feat);
56 }
57
58 bool extract_alpha_component (Mesh::component &c, Polygon &poly, bool verbose)
59 {
60     Mesh::vertex_circulator v(c);
61     Vertex_handle v0(*v);
62     Ring ring;
63
64     do {
65         ring.push_back(v->point());
66     } while (*(++v) != v0);
67
68     if (ring.size() < 4) {
69         if (verbose) cerr << "Discarding degenerate shape." << endl;
70         return false;
71     }
72     if (verbose)
73         cerr << "- " << ring.size() << " vertices, "
74              << "area: " << fabs(ring.area())
75              << ", perimeter: " << ring.perimeter() << endl;
76
77     poly.push_back(ring);
78     return true;
79 }
80
81 void construct_alpha_shape (Config &config, Shapefile &shape,
82                             vector<Point> &pts, string text)
83 {
84     coord_type alpha = config.alpha;
85     Mesh mesh(pts.begin(), pts.end(), alpha);
86
87     if (alpha == 0) {
88         if (config.verbose) cerr << "Computing optimal alpha... ";
89         alpha = *mesh.find_optimal_alpha(1);
90         if (config.verbose) cerr << alpha << endl;
91         mesh.set_alpha(alpha);
92     }
93
94     if (config.verbose)
95         cerr << mesh.number_of_solid_components() <<
96               " component(s) found for alpha value " << alpha << "." << endl;
97
98     for (Mesh::component c = mesh.components_begin();
99          c != mesh.components_end(); c++) {
100         Polygon poly;
101         if (extract_alpha_component(c, poly, config.verbose)) {
102             if (config.verbose)
103                 cerr << "Writing polygon for tag '" << text << "'." << endl;
104             write_polygon_to_shapefile(shape, poly, text, pts.size());
105         }
106     }
107 }
108
109 void construct_output (Config &config, Shapefile &shape,
110                        vector<Point> &pts, string tag) {
111     cerr << "Got " << pts.size() << " points for tag '"
112                    << tag << "'." << endl;
113     if (config.points_only) {
114         write_points_to_shapefile(shape, pts, tag);
115     } else {
116         if (pts.size() >= 3) {
117             construct_alpha_shape(config, shape, pts, tag);
118         } else {
119             if (config.verbose)
120                 cerr << "Need more than 3 points for tag '"
121                      << tag << "'" << endl;
122         }
123     }
124 }
125
126 void display_usage (void) {
127     cerr << endl
128          << "clustr "  << VERSION << " - construct polygons from tagged points"
129          << endl
130          << "written by " << AUTHOR << endl
131          << COPYRIGHT  << endl
132          << endl
133          << "Usage: clustr [-a <n>] [-p] [-v] <input> <output>" << endl
134          << "   -h, -?      this help message" << endl
135          << "   -v          be verbose (default: off)" << endl
136          << "   -a <n>      set alpha value (default: use \"optimal\" value)"
137          << endl
138          << "   -p          output points to shapefile, instead of polygons"
139          << endl
140          << endl
141          << "If <input> is missing or given as \"-\", stdin is used."
142          << endl
143          << "If <output> is missing, output is written to clustr.shp."
144          << endl
145          << "Input file should be formatted as: <tag> <lon> <lat>\\n" << endl
146          << "Tags must not contain spaces." << endl
147          << endl;
148 }
149
150 bool parse_options (Config &config, int argc, char **argv) {
151     istringstream iss;
152     string optstring("a:pvh?");
153
154     if (argc <= 1) {
155         display_usage();
156         return false;
157     }
158
159     int opt = getopt( argc, argv, optstring.c_str() );
160     while( opt != -1 ) {
161         switch( opt ) {
162             case 'a':
163                 iss.str((string) optarg);
164                 iss >> config.alpha;
165                 break;
166             case 'p':
167                 config.points_only = true;
168                 break;
169             case 'v':
170                 config.verbose = true;
171                 break;
172             case 'h':   /* fall-through is intentional */
173             case '?':
174                 display_usage();
175                 return false;
176         }
177         opt = getopt( argc, argv, optstring.c_str() );
178     }
179     if (optind < argc) {
180         config.in_file = argv[optind++];
181     }
182     if (optind < argc) {
183         config.out_file = argv[optind];
184     }
185     return true;
186 }
187
188 int main(int argc, char **argv) {
189     vector<Point> points;
190     istream *input;
191     Config config;
192     Shapefile *shape;
193
194     if (!parse_options(config, argc, argv))
195         return 1;
196
197     if (config.in_file == "-") {
198         input = &cin;
199     } else {
200         input = new ifstream(config.in_file.c_str());
201     }
202    
203     if (config.points_only) {
204         if (config.verbose) {
205             cerr << "Only converting points to Shapefile,"
206                  << " no clustering performed." << endl;
207         }
208         shape = new Shapefile(config.out_file, wkbPoint);
209         shape->add_field("tag", OFTString, 64);
210        
211     } else {
212         shape = new Shapefile(config.out_file, wkbPolygon);
213         shape->add_field("tag", OFTString, 64);
214         shape->add_field("count", OFTInteger, 8);
215         shape->add_field("area", OFTReal, 10, 2);
216         shape->add_field("perimeter", OFTReal, 10, 2);
217         shape->add_field("density", OFTReal, 10, 2);
218     }
219
220     string line, tag, previous = "";
221     vector<Point> pts;
222     Point pt;
223    
224     if (config.verbose)
225         cerr << "Reading points from input." << endl;
226
227     while (!getline(*input,line).eof()) {
228         istringstream is(line);
229         if (!(is >> tag >> pt)) {
230             cerr << "Bad input line: " << line;
231             continue;
232         }
233         if (previous == "") {
234             previous = tag;
235         }
236         if (tag != previous) {
237             construct_output(config, *shape, pts, tag);
238             pts.clear();
239         }
240         pts.push_back(pt);
241     }
242     construct_output(config, *shape, pts, tag);
243    
244     // if you want to do memory leak testing, include <ogr_api.h> and
245     // uncomment the following line:
246     // OGRCleanupAll();
247     delete shape;
248     return 0;
249 }
250
Note: See TracBrowser for help on using the browser.