Graphviz 16.0.1~dev.20260815.2250
Loading...
Searching...
No Matches
gvmap.c
Go to the documentation of this file.
1
6/*************************************************************************
7 * Copyright (c) 2011 AT&T Intellectual Property
8 * All rights reserved. This program and the accompanying materials
9 * are made available under the terms of the Eclipse Public License v2.0
10 * which accompanies this distribution, and is available at
11 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
12 *
13 * Contributors: Details at https://graphviz.org
14 *************************************************************************/
15
16#include "config.h"
17#include "../tools/openFile.h"
18#include <stdbool.h>
19#include <stdio.h>
20#include <stdlib.h>
21#define STANDALONE
22#include <sparse/general.h>
23#include <sparse/QuadTree.h>
24#include <time.h>
25#include <sparse/SparseMatrix.h>
26#include <getopt.h>
27#include <string.h>
28#include "make_map.h"
31#include <neatogen/overlap.h>
32#include <sparse/clustering.h>
33#include <cgraph/ingraphs.h>
34#include <sparse/DotIO.h>
35#include <sparse/colorutil.h>
37#include <util/startswith.h>
38#include <util/unreachable.h>
39
40typedef struct {
41 char* cmd;
42 char **infiles;
43 FILE* outfile;
45 int nrandom;
51 double line_width;
53 char opacity[3];
55 int nart;
58 int nedgep;
59 const char *line_color;
62 int seed; /* seed used to calculate Fiedler vector */
63} params_t;
64
65static const char usestr[] =
66" where graphfile must contain node positions, and widths and heights for each node. No overlap between nodes should be present. Acceptable options are: \n\
67 -a k - average number of artificial points added along the bounding box of the labels. If < 0, a suitable value is selected automatically. (-1)\n\
68 -b v - polygon line width, with v < 0 for no line. (0)\n\
69 -c k - polygon color scheme (1)\n\
70 0 : no polygons\n\
71 1 : pastel (default)\n\
72 2 : blue to yellow\n\
73 3 : white to red\n\
74 4 : light grey to red\n\
75 5 : primary colors\n\
76 6 : sequential single hue red \n\
77 7 : Adam color scheme\n\
78 8 : Adam blend\n\
79 9 : sequential single hue lighter red \n\
80 10 : light grey\n\
81 -c_opacity=xx - 2-character hex string for opacity of polygons\n\
82 -C k - generate at most k clusters. (0)\n\
83 -d s - seed used to calculate Fiedler vector for optimal coloring\n\
84 -D - use top-level cluster subgraphs to specify clustering\n\
85 -e - show edges\n\
86 -h k - number of artificial points added to maintain bridge between endpoints (0)\n\
87 -highlight=k - only draw cluster k\n\
88 -k - increase randomness of boundary\n\
89 -m v - bounding box margin. If 0, auto-assigned (0)\n\
90 -o <file> - put output in <file> (stdout)\n\
91 -O - do NOT do color assignment optimization that maximizes color difference between neighboring countries\n\
92 -r k - number of random points k used to define sea and lake boundaries. If 0, auto assigned. (0)\n\
93 -s v - depth of the sea and lake shores in points. If < 0, auto assigned. (0)\n\
94 -t n - improve contiguity up to n times. (0)\n\
95 -v - verbose\n\
96 -z c - polygon line color (black)\n";
97
98/*
99
100 -q f - output format (3)\n\
101 0 : Mathematica\n\
102 1 : PostScript\n\
103 2 : country map\n\
104 3 : dot format\n\
105*/
106 /* e.g.,
107 1 [cluster=10, clustercolor="#ff0000"]
108 2 [cluster=10]
109 (and no other nodes are in cluster10)
110
111 then since we can only use 1 color for the cluster 10, both 1 and 2 will be colored based on the color of node 2. However if you have
112
113 2 [cluster=10]
114 1 [cluster=10, clustercolor="#ff0000"]
115
116 then you get both colored red.
117
118 */
119
120static void usage(char* cmd, int eval)
121{
122 fprintf(stderr, "Usage: %s <options> graphfile\n", cmd);
123 fputs (usestr, stderr);
125}
126
127#define HLPFX "ighlight="
128#define N_HLPFX (sizeof(HLPFX)-1)
129
130static void
131init(int argc, char **argv, params_t* pm)
132{
133 char* cmd = argv[0];
134 int c;
135 double s;
136 int v, r;
137
138 pm->outfile = NULL;
139 pm->opacity[0] = '\0';
141 pm->nrandom = -1;
142 pm->shore_depth_tol = 0;
143 pm->highlight_cluster = 0;
144 pm->useClusters = 0;
146 pm->plotedges = false;
148 pm->line_width = 0;
149 pm->improve_contiguity_n = 0;
150 pm->nart = -1;
151 pm->color_optimize = true;
152 pm->maxcluster = 0;
153 pm->nedgep = 0;
154
155 pm->cmd = cmd;
156 pm->infiles = NULL;
157 pm->line_color = "#000000";
158 pm->include_OK_points = false;
159 pm->seed = 123;
160
161 pm->bbox_margin = 0;
162
163 opterr = 0;
164 while ((c = getopt(argc, argv, ":evODQko:m:s:r:c:C:b:t:a:h:z:d:?")) != -1) {
165 switch (c) {
166 case 'm':
167 if (sscanf(optarg, "%lf", &s) > 0 && s != 0) {
168 pm->bbox_margin = s;
169 } else {
170 usage(cmd, 1);
171 }
172 break;
173 case 'Q':
175 break;
176 case 's':
177 if (sscanf(optarg, "%lf", &s) > 0) {
178 pm->shore_depth_tol = s;
179 } else {
180 usage(cmd,1);
181 }
182 break;
183 case 'h':
184 if (sscanf(optarg, "%d", &v) > 0) {
185 pm->nedgep = MAX(0, v);
186 } else if (startswith(optarg, HLPFX) &&
187 sscanf(optarg + N_HLPFX, "%d", &v) > 0) {
188 pm->highlight_cluster = MAX(0, v);
189 } else {
190 usage(cmd,1);
191 }
192 break;
193 case 'r':
194 if (sscanf(optarg, "%d", &r) > 0) {
195 pm->nrandom = r;
196 }
197 break;
198 case 't':
199 if (sscanf(optarg, "%d", &r) > 0 && r > 0) {
200 pm->improve_contiguity_n = r;
201 }
202 break;
203 case 'k':
204 pm->include_OK_points = true;
205 break;
206 case 'v':
207 Verbose = 1;
208 break;
209 case 'D':
210 pm->useClusters = 1;
211 break;
212 case 'e':
213 pm->plotedges = true;
214 break;
215 case 'o':
216 pm->outfile = openFile(pm->cmd, optarg, "w");
217 break;
218 case 'O':
219 pm->color_optimize = false;
220 break;
221 case 'a':
222 if (sscanf(optarg, "%d", &r) > 0) {
223 pm->nart = r;
224 } else {
225 usage(cmd,1);
226 }
227 break;
228 case 'c': {
229 char stmp[3]; // two character string plus '\0'
230 if (sscanf(optarg,"_opacity=%2s", stmp) > 0 && strlen(stmp) == 2){
231 memcpy(pm->opacity, stmp, sizeof(pm->opacity));
232 } else if (sscanf(optarg, "%d", &r) > 0 && r >= COLOR_SCHEME_NONE &&
233 r <= COLOR_SCHEME_GREY) {
234 pm->color_scheme = r;
235 } else if (knownColorScheme(optarg)) {
237 pm->color_scheme_str = optarg;
238 } else {
239 fprintf(stderr,"-c option %s is invalid, must be a valid integer or string\n", optarg);
240 usage(cmd, 1);
241 }
242 break;
243 }
244 case 'd':
245 if (sscanf(optarg,"%d",&v) <= 0){
246 usage(cmd,1);
247 }
248 else
249 pm->seed = v;
250 break;
251 case 'C':
252 if (!(sscanf(optarg, "%d", &v) > 0 && v >= 0)) {
253 usage(cmd,1);
254 }
255 else
256 pm->maxcluster = v;
257 break;
258 case 'z': {
259 pm->line_color = optarg;
260 break;
261 }
262 case 'b':
263 if (sscanf(optarg,"%lf",&s) > 0) {
264 pm->line_width = s;
265 } else {
266 fprintf (stderr, "%s: unexpected argument \"%s\" for -b flag\n", cmd, optarg);
267 }
268 break;
269 case ':':
270 fprintf(stderr, "gvpack: option -%c missing argument - ignored\n", optopt);
271 break;
272 case '?':
273 if (optopt == '\0' || optopt == '?')
274 usage(cmd, 0);
275 else {
276 fprintf(stderr, " option -%c unrecognized\n", optopt);
277 usage(cmd, 1);
278 }
279 break;
280 default:
281 UNREACHABLE();
282 }
283 }
284
285 argv += optind;
286 argc -= optind;
287 if (argc)
288 pm->infiles = argv;
289 if (!pm->outfile)
290 pm->outfile = stdout;
291}
292
293static int
294validateCluster (int n, int* grouping, int clust_num)
295{
296 int i;
297 for (i = 0; i < n; i++) {
298 if (grouping[i] == clust_num) return clust_num;
299 }
300 fprintf (stderr, "Highlighted cluster %d not found - ignored\n", clust_num);
301 return 0;
302}
303
305static int makeMap(SparseMatrix graph, int n, double *x, double *width,
306 int *grouping, float *rgb_r,
307 float *rgb_g, float *rgb_b, params_t *pm, Agraph_t *g) {
308 const int dim = 2;
309 int i;
310 SparseMatrix poly_lines, polys, poly_point_map;
311 int nverts, *polys_groups;
312 double *x_poly;
313 SparseMatrix country_graph = NULL;
314 int improve_contiguity_n = pm->improve_contiguity_n;
315#ifdef TIME
316 clock_t cpu;
317#endif
318 int nart0;
319 int nart, nrandom;
320
321#ifdef TIME
322 cpu = clock();
323#endif
324 nrandom = pm->nrandom; nart0 = nart = pm->nart;
325 if (pm->highlight_cluster) {
326 pm->highlight_cluster = validateCluster (n, grouping, pm->highlight_cluster);
327 }
329 grouping, graph, pm->bbox_margin, nrandom,
330 &nart, pm->nedgep, pm->shore_depth_tol,
331 &nverts, &x_poly, &poly_lines, &polys,
332 &polys_groups, &poly_point_map,
333 &country_graph, pm->highlight_cluster)
334 != 0) {
335 return -1;
336 }
337
338 if (Verbose) fprintf(stderr,"nart = %d\n",nart);
339 /* compute a good color permutation */
340 if (pm->color_optimize && country_graph && rgb_r && rgb_g && rgb_b)
341 map_optimal_coloring(pm->seed, country_graph, rgb_r, rgb_g, rgb_b);
342 else if (pm->color_scheme_str){
344 &rgb_r, &rgb_g, &rgb_b);
345 }
346
347#ifdef TIME
348 fprintf(stderr, "map making time = %f\n",((double) (clock() - cpu)) / CLOCKS_PER_SEC);
349#endif
350
351
352 /* now we check to see if all points in the same group are also in the same polygon, if not, the map is not very
353 contiguous so we move point positions to improve contiguity */
354 if (graph && improve_contiguity_n) {
355 for (i = 0; i < improve_contiguity_n; i++){
356 improve_contiguity(n, grouping, poly_point_map, x, graph);
357 nart = nart0;
359 n, x, width, grouping, graph, pm->bbox_margin, nrandom, &nart, pm->nedgep,
360 pm->shore_depth_tol, &nverts, &x_poly, &poly_lines,
361 &polys, &polys_groups, &poly_point_map, &country_graph, pm->highlight_cluster);
362 }
363 {
365 remove_overlap(dim, D, x, width, 1000, 5000.,
366 ELSCHEME_NONE, 0, NULL, NULL, true);
368
369 nart = nart0;
371 n, x, width, grouping, graph, pm->bbox_margin, nrandom, &nart, pm->nedgep,
372 pm->shore_depth_tol, &nverts, &x_poly, &poly_lines,
373 &polys, &polys_groups, &poly_point_map, &country_graph, pm->highlight_cluster);
374 }
375
376 }
377
378 Dot_SetClusterColor(g, rgb_r, rgb_g, rgb_b, grouping);
379 plot_dot_map(g, polys, poly_lines, pm->line_width, pm->line_color, x_poly, polys_groups, rgb_r, rgb_g, rgb_b, pm->opacity,
380 (pm->plotedges?graph:NULL), pm->outfile);
381 SparseMatrix_delete(polys);
382 SparseMatrix_delete(poly_lines);
383 SparseMatrix_delete(poly_point_map);
384 SparseMatrix_delete(country_graph);
385 free(x_poly);
386 free(polys_groups);
387 return 0;
388}
389
391static int mapFromGraph(Agraph_t *g, params_t *pm) {
393 int n;
394 double* width = NULL;
395 double *x = NULL;
396 int *grouping = NULL;
397 float* rgb_r = NULL;
398 float* rgb_g = NULL;
399 float* rgb_b = NULL;
400
401 initDotIO(g);
402 graph = Import_coord_clusters_from_dot(g, pm->maxcluster, &n, &width, &x, &grouping,
403 &rgb_r, &rgb_g, &rgb_b, pm->color_scheme, pm->clusterMethod, pm->useClusters);
404 int rc;
405 if (x != NULL) {
406 rc = makeMap(graph, n, x, width, grouping, rgb_r, rgb_g, rgb_b,
407 pm, g);
408 } else { // the graph was missing position information
409 rc = -1;
410 }
412 free(rgb_r);
413 free(rgb_g);
414 free(rgb_b);
415 free(grouping);
416 free(x);
417 free(width);
418 return rc;
419}
420
421int main(int argc, char *argv[])
422{
423 params_t pm;
424 Agraph_t* g;
425 Agraph_t* prevg = NULL;
426 ingraph_state ig;
427
428 init(argc, argv, &pm);
429
430 newIngraph(&ig, pm.infiles);
431 while ((g = nextGraph (&ig)) != 0) {
432 if (prevg) agclose (prevg);
433 if (mapFromGraph(g, &pm) != 0) {
434 graphviz_exit(EXIT_FAILURE);
435 }
436 prevg = g;
437 }
438
439 graphviz_exit(0);
440}
441
void Dot_SetClusterColor(Agraph_t *g, float *rgb_r, float *rgb_g, float *rgb_b, int *clusters)
Definition DotIO.c:245
SparseMatrix Import_coord_clusters_from_dot(Agraph_t *g, int maxcluster, int *nn, double **label_sizes, double **x, int **clusters, float **rgb_r, float **rgb_g, float **rgb_b, int default_color_scheme, int clustering_scheme, int useClusters)
Definition DotIO.c:265
void initDotIO(Agraph_t *g)
Definition DotIO.c:621
@ COLOR_SCHEME_PASTEL
Definition DotIO.h:23
@ COLOR_SCHEME_NONE
Definition DotIO.h:22
@ COLOR_SCHEME_GREY
Definition DotIO.h:32
void SparseMatrix_delete(SparseMatrix A)
SparseMatrix SparseMatrix_get_real_adjacency_matrix_symmetrized(SparseMatrix A)
#define MAX(a, b)
Definition arith.h:33
@ CLUSTERING_MODULARITY
Definition clustering.h:39
@ CLUSTERING_MQ
Definition clustering.h:39
static char * cmd
Definition acyclic.c:42
bool knownColorScheme(const char *name)
static NORETURN void graphviz_exit(int status)
Definition exit.h:23
static int eval(Agraph_t *g, int root)
Definition gc.c:271
static bool Verbose
Definition gml2gv.c:26
void free(void *)
node NULL
Definition grammar.y:181
int agclose(Agraph_t *g)
deletes a graph, freeing its associated storage
Definition graph.c:97
Agraph_t * graph(char *name)
Definition gv.cpp:34
static void init(int argc, char **argv, params_t *pm)
Definition gvmap.c:131
static const char usestr[]
Definition gvmap.c:65
#define N_HLPFX
Definition gvmap.c:128
static int mapFromGraph(Agraph_t *g, params_t *pm)
Definition gvmap.c:391
static int validateCluster(int n, int *grouping, int clust_num)
Definition gvmap.c:294
#define HLPFX
Definition gvmap.c:127
static int makeMap(SparseMatrix graph, int n, double *x, double *width, int *grouping, float *rgb_r, float *rgb_g, float *rgb_b, params_t *pm, Agraph_t *g)
Definition gvmap.c:305
static const char * usage
Definition gvpr.c:54
#define D
Definition hierarchy.c:122
Agraph_t * nextGraph(ingraph_state *sp)
Definition ingraphs.c:61
ingraph_state * newIngraph(ingraph_state *sp, char **files)
Definition ingraphs.c:140
supports user-supplied data
void map_palette_optimal_coloring(char *color_scheme, SparseMatrix A0, float **rgb_r, float **rgb_g, float **rgb_b)
Definition make_map.c:40
void plot_dot_map(Agraph_t *gr, SparseMatrix polys, SparseMatrix poly_lines, double line_width, const char *line_color, double *x_poly, int *polys_groups, float *r, float *g, float *b, const char *opacity, SparseMatrix A, FILE *f)
Definition make_map.c:304
int make_map_from_rectangle_groups(bool include_OK_points, int n, double *x, double *sizes, int *grouping, SparseMatrix graph, double bounding_box_margin, int nrandom, int *nart, int nedgep, double shore_depth_tol, int *nverts, double **x_poly, SparseMatrix *poly_lines, SparseMatrix *polys, int **polys_groups, SparseMatrix *poly_point_map, SparseMatrix *country_graph, int highlight_cluster)
Definition make_map.c:1192
void improve_contiguity(int n, int *grouping, SparseMatrix poly_point_map, double *x, SparseMatrix graph)
Definition make_map.c:119
void map_optimal_coloring(int seed, SparseMatrix A, float *rgb_r, float *rgb_g, float *rgb_b)
Definition make_map.c:98
static const int dim
static FILE * openFile(const char *argv0, const char *name, const char *mode)
Definition openFile.h:8
void remove_overlap(int dim, SparseMatrix A, double *x, double *label_sizes, int ntry, double initial_scaling, int edge_labeling_scheme, int n_constr_nodes, int *constr_nodes, SparseMatrix A_constr, bool do_shrinking)
Definition overlap.c:588
@ ELSCHEME_NONE
Definition overlap.h:17
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
graph or subgraph
Definition cgraph.h:424
char * color_scheme_str
Definition gvmap.c:52
const char * line_color
Definition gvmap.c:59
int highlight_cluster
Definition gvmap.c:61
int clusterMethod
Definition gvmap.c:48
int color_scheme
Definition gvmap.c:50
bool color_optimize
Definition gvmap.c:56
char * cmd
Definition gvmap.c:41
bool plotedges
Definition gvmap.c:49
int useClusters
Definition gvmap.c:47
int nrandom
Definition gvmap.c:45
char ** infiles
Definition gvmap.c:42
double shore_depth_tol
Definition gvmap.c:44
int nart
Definition gvmap.c:55
char opacity[3]
Definition gvmap.c:53
double bbox_margin
Definition gvmap.c:46
int improve_contiguity_n
Definition gvmap.c:54
FILE * outfile
Definition gvmap.c:43
int maxcluster
Definition gvmap.c:57
int nedgep
Definition gvmap.c:58
int seed
Definition gvmap.c:62
bool include_OK_points
Definition gvmap.c:60
double line_width
Definition gvmap.c:51
int main()
Definition grammar.c:90
#define UNREACHABLE()
Definition unreachable.h:30