f41b982009-05-07Martin Stjernholm // The Atlas module. Copyright © 1999 - 2009, Roxen IS.
77f4fa2000-12-02Martin Nilsson // // Please note: The map is incomplete and incorrect in details. Countries // and territories are missing. #include <module.h> inherit "module";
0917d32013-03-04Anders Johansson constant cvs_version = "$Id$";
77f4fa2000-12-02Martin Nilsson constant thread_safe = 1; constant module_type = MODULE_TAG | MODULE_EXPERIMENTAL;
bc0fa02001-03-08Per Hedbor constant module_name = "Graphics: Atlas";
77f4fa2000-12-02Martin Nilsson constant module_doc = #"Provides the <tt>&lt;atlas&gt;</tt> tag that creates a world map. It is possible to highlight countries on the generated world map."; roxen.ImageCache the_cache;
ceab022002-11-15Jonas Wallden int do_ext;
77f4fa2000-12-02Martin Nilsson  void start() { the_cache = roxen.ImageCache( "atlas", generate_image );
ceab022002-11-15Jonas Wallden  do_ext = query("ext"); }
c7ba992010-04-27Henrik Grubbström (Grubba) void stop() { destruct(the_cache); }
ceab022002-11-15Jonas Wallden void create() { defvar("ext", Variable.Flag(0, VAR_MORE, "Append format to generated images", "Append the image format (.gif, .png, " ".jpg, etc) to the generated images. " "This is not necessary, but might seem " "nicer, especially to people who try " "to mirror your site."));
77f4fa2000-12-02Martin Nilsson } string status() { array s=the_cache->status(); return sprintf("<b>Images in cache:</b> %d images<br />\n<b>Cache size:</b> %s", s[0]/2, Roxen.sizetostring(s[1])); } mapping(string:function) query_action_buttons() {
da43042005-12-16Jonas Wallden  return ([ "Clear Cache":flush_cache ]);
77f4fa2000-12-02Martin Nilsson } void flush_cache() { the_cache->flush(); } mapping find_internal( string f, RequestID id ) {
3e5a402002-11-19Jonas Wallden  // Strip file extensions from filename. Since "." isn't a valid character // in the ID we can split at the first occurrence. return the_cache->http_file_answer((f / ".")[0], id);
77f4fa2000-12-02Martin Nilsson } // ---------------------- Tags ------------------------------ class TagEmitAtlas { inherit RXML.Tag; constant name = "emit"; constant plugin_name = "atlas"; array get_dataset( mapping args, RequestID id ) { if (args->list=="countries") return map(Map.Earth()->countries(), lambda(string c) { return (["name":c]); }); if (args->list=="regions") return map(Map.Earth()->regions(), lambda(string c) { return (["name":c]); }); RXML.parse_error("No valid list argument given\n"); } } constant imgargs = ({ "region", "fgcolor", "bgcolor" }); class TagAtlas { inherit RXML.Tag; constant name = "atlas"; class TagCountry { inherit RXML.Tag; constant name = "country";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
77f4fa2000-12-02Martin Nilsson  class Frame { inherit RXML.Frame; array do_enter(RequestID id) { string name; if(args->domain) name = Map.domain_to_country[lower_case(args->domain)]; else if(args->name) name = Map.aliases[lower_case(args->name)] || args->name; if(!name) parse_error("No domain or name attribute given.\n"); id->misc->atlas_state->color[lower_case(name)] = parse_color(args->color || "#e0c080"); return 0; } } } class TagMarker { inherit RXML.Tag; constant name = "marker";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
77f4fa2000-12-02Martin Nilsson  class Frame { inherit RXML.Frame; array do_enter(RequestID id) { if(args->x[-1]=='%') args->x = (float)args->x/100.0 * args->width; args->x = (int)args->x; if(args->y[-1]=='%') args->y = (float)args->y/100.0 * args->height; args->y = (int)args->y; if(args->x<0 || args->y<0 || args->x>=id->misc->atlas_state->width || args->y>=id->misc->atlas_state->height) return 0; args->size = (int)args->size || 4; if(args->color) args->color = parse_color(args->color); else args->color = ({ 255, 0, 0 }); id->misc->atlas_state->markers += ({ args }); return 0; } } }
dd9a412001-08-24Martin Stjernholm  // This tag set can probably be shared, but I don't know for sure. /mast RXML.TagSet internal = RXML.TagSet(this_module(), "atlas",
77f4fa2000-12-02Martin Nilsson  ({ TagCountry(), TagMarker() }) ); class Frame { inherit RXML.Frame; RXML.TagSet additional_tags = internal; array do_enter(RequestID id) { // Construct a state which will be used // in order to draw the image later on. mapping state = ([ "color":([]), "markers":({}) ]); // Calculate size of image. Preserve // image aspect ratio if possible. int w, h; if(args->width) { sscanf(args->width, "%d", w); h = (int)(w*3.0/5.0); sscanf(args->height||"", "%d", h); } else { sscanf(args->height||"300", "%d", h); w = (int)(h*5.0/3.0); }
6593132001-06-22Martin Nilsson  state->width = w; state->height = h; args->width = (string)w; args->height = (string)h;
77f4fa2000-12-02Martin Nilsson  foreach( imgargs, string arg) if(args[arg]) state[arg]=m_delete(args, arg); id->misc->atlas_state = state; } array do_return(RequestID id) { mapping state = id->misc->atlas_state;
ce4deb2009-11-26Henrik Grubbström (Grubba)  int timeout = Roxen.timeout_dequantifier(args);
9363fa2009-11-24Henrik Grubbström (Grubba) 
db053e2000-12-05Martin Nilsson  args->src = query_absolute_internal_location(id) +
9363fa2009-11-24Henrik Grubbström (Grubba)  the_cache->store(state, id, timeout);
ceab022002-11-15Jonas Wallden  if(do_ext) args->src += ".gif";
77f4fa2000-12-02Martin Nilsson  if(!args->alt) args->alt = state->region || "The World";
6593132001-06-22Martin Nilsson 
77f4fa2000-12-02Martin Nilsson  result = RXML.t_xml->format_tag("img", args); return 0; } } }
bd44e22003-01-22Henrik Grubbström (Grubba) Image.Image generate_image(mapping state, RequestID id)
77f4fa2000-12-02Martin Nilsson { if(!state) return 0; state->fgcolor = parse_color(state->fgcolor || "#ffffff"); mapping opt = ([ "color_fu": lambda(string name) { return state->color[name] || state->fgcolor; } ]); if(state->bgcolor) opt->color_sea = parse_color(state->bgcolor); if(state->markers) opt->markers = state->markers; Map.Earth m = Map.Earth( state->region );
bd44e22003-01-22Henrik Grubbström (Grubba)  Image.Image img = m->image(state->width, state->height, opt);
77f4fa2000-12-02Martin Nilsson  return img; } TAGDOCUMENTATION; #ifdef manual constant tagdoc=([
ce8fb02001-09-21Johan Sundström "emit#atlas": ({ #"<desc type='plugin'><p><short>
10ed282001-08-24Martin Nilsson  Lists regions and countries defined in the atlas tag map.</short></p>
9b03652001-03-07Kenneth Johansson </desc> <attr name='list' value='regions|countries'><p>
10ed282001-08-24Martin Nilsson  Select what type of objects to list.</p>
829e722001-03-13Kenneth Johansson 
3f0e712001-09-03Martin Nilsson <ex>
10ed282001-08-24Martin Nilsson <b>Available regions</b><br />
829e722001-03-13Kenneth Johansson <emit source='atlas' list='regions'>
3f0e712001-09-03Martin Nilsson &_.name;<br />
829e722001-03-13Kenneth Johansson </emit> </ex>
9b03652001-03-07Kenneth Johansson </attr>", ([
ce8fb02001-09-21Johan Sundström  "&_.name;":#"<desc type='entity'><p>
9b03652001-03-07Kenneth Johansson  The name of the region/country</p> </desc>" ])
77f4fa2000-12-02Martin Nilsson  }),
ce8fb02001-09-21Johan Sundström "atlas":({ #"<desc type='cont'><p><short>
829e722001-03-13Kenneth Johansson  Draws a map.</short> The map shows either the world, regions (Africa, Europe,
7209722012-05-09Jenny Dalenius  etc) or countries. It's a known bug that the map is not entierly up to date. It is possible to pass attributes, such as the alt attribute, to the resulting tag by including them in the atlas tag.</p>
829e722001-03-13Kenneth Johansson 
3f0e712001-09-03Martin Nilsson <ex><atlas/></ex>
10ed282001-08-24Martin Nilsson 
3f0e712001-09-03Martin Nilsson <ex><atlas fgcolor='#425A84' bgcolor='#dee2eb'>
10ed282001-08-24Martin Nilsson <country domain='se' color='orange'/> <country domain='jp' color='orange'/> <marker x='100' y='90'/> </atlas> </ex>
9b03652001-03-07Kenneth Johansson </desc>
829e722001-03-13Kenneth Johansson <attr name='region' value='name' default='The World'><p>
9b03652001-03-07Kenneth Johansson  Which map to show. The value may be any of the listed region values
10ed282001-08-24Martin Nilsson  that emit plugin <xref href='../output/emit_atlas.tag'>atlas</xref>
9b03652001-03-07Kenneth Johansson  returns.</p>
10ed282001-08-24Martin Nilsson 
3f0e712001-09-03Martin Nilsson <ex><atlas region='europe' width='200'/></ex>
9b03652001-03-07Kenneth Johansson </attr> <attr name='width' value='number'><p> The width of the image.</p> </attr> <attr name='height' value='number'><p> The height of the image.</p> </attr>
10ed282001-08-24Martin Nilsson <attr name='fgcolor' value='color' default='white'><p>
9b03652001-03-07Kenneth Johansson  The color of the unselected land areas.</p> </attr>
10ed282001-08-24Martin Nilsson <attr name='bgcolor' value='color' default='#101040'><p> The color of the sea areas.</p>
5adc582009-11-30Henrik Grubbström (Grubba) </attr> <h1>Timeout</h1> <p>The generated image will by default never expire, but in some circumstances it may be pertinent to limit the time the image and its associated data is kept. Its possible to set an (advisory) timeout on the image data using the following attributes.</p> <attr name='unix-time' value='number'><p> Set the base expiry time to this absolute time.</p><p> If left out, the other attributes are relative to current time.</p> </attr> <attr name='years' value='number'><p> Add this number of years to the time this entry is valid.</p> </attr> <attr name='months' value='number'><p> Add this number of months to the time this entry is valid.</p> </attr> <attr name='weeks' value='number'><p> Add this number of weeks to the time this entry is valid.</p> </attr> <attr name='days' value='number'><p> Add this number of days to the time this entry is valid.</p> </attr> <attr name='hours' value='number'><p> Add this number of hours to the time this entry is valid.</p> </attr> <attr name='beats' value='number'><p> Add this number of beats to the time this entry is valid.</p> </attr> <attr name='minutes' value='number'><p> Add this number of minutes to the time this entry is valid.</p> </attr> <attr name='seconds' value='number'><p> Add this number of seconds to the time this entry is valid.</p>
9b03652001-03-07Kenneth Johansson </attr>", ([ "country" : #"<desc tag='tag'><p><short>
10ed282001-08-24Martin Nilsson  A region that should be highlighted with a different color on the map.</short></p>
9b03652001-03-07Kenneth Johansson </desc> <attr name='domain' value='name'><p> The top domain of the country that should be highlighted.</p> </attr> <attr name='name' value='name'><p>
10ed282001-08-24Martin Nilsson  The name of the country that should be highlighted. A list of available names can be aquired from the <xref href='../output/emit_atlas.tag'>atlas</xref> emit plugin.</p></attr>
9b03652001-03-07Kenneth Johansson 
10ed282001-08-24Martin Nilsson <attr name='color' value='color' default='#e0c080'><p>
9b03652001-03-07Kenneth Johansson  The color that should be used for highlighting.</p> </attr>", "marker" : #"<desc tag='tag'><p><short> Draws a marker at the specified position</short></p> </desc> <attr name='x' value='pixels or percentage' required='required'><p> The distance from the left of the map.</p> </attr> <attr name='y' value='pixels or percentage' required='required'><p> The distance from the top of the map.</p> </attr> <attr name='color' value='color' default='red'><p> The color of the marker</p> </attr>
10ed282001-08-24Martin Nilsson <attr name='style' value='box|diamond' default='diamond'><p>
9b03652001-03-07Kenneth Johansson  The type of marker.</p>
10ed282001-08-24Martin Nilsson  <ex><atlas region='europe' width='150'> <marker x='100' y='30' style='diamond' /> <marker x='125' y='30' style='box' /> </atlas> </ex>
9b03652001-03-07Kenneth Johansson </attr>
10ed282001-08-24Martin Nilsson <attr name='size' value='number' default='4'><p>
9b03652001-03-07Kenneth Johansson  The size of the marker.</p> </attr>"
77f4fa2000-12-02Martin Nilsson  ]) }), ]); #endif