Pasta dough recipe

1 cup flour (I’ve been using white whole wheat)

1 egg

~2tbps cool water

  1. Mix flour and egg, adding water until flour is just moistened and sticks together

  2. Let rest for 5 minutes

  3. Knead the dough for 2 minutes. Or you can do it for 10 minutes; more kneading means better developed gluten and a “smoother” noodle.

  4. Let rest 10 minutes

  5. Roll out or whatever

(adapted from a cooks.com recipe


Hemp milk

Info on Hemp Milk.

I just bought some of this (Manitoba Harvest brand, the chocolate kind) at Whole Foods. It’s not soy milk, but I’m not sure if it’s any better. I kind’ve got a bit of a choke in the back of my throat on the first sip, though after that it was pretty smooth.


Fears of death

Thoughts of mortality make people entrench their beliefs. (via boingboing). It also makes people act in ways that boost their self-esteem, which, importantly, affects their consumer behavior (via google).

These seem to both be domestic studies. I wonder how the US compares internationally in “mortality awareness”. (I’m guessing pretty low).


Verb to do

A great list of functional verbs for writing resumes or their reverse situational equivalent: job descriptions. (via Google)

ADMINISTRATIVE ACTION VERBS

advise Offer an informed opinion or give specialized information to others.
adapt Modify or change to fit specific or new situations.
administer Manage or direct. (Generally requires some additional explanation to show specific detail.) See manage.
appoint To set officially, arrange.
approve Exercise final and decisive authority, causing action to use money, manpower, materials, or equipment.
arrange To make preparations for, to plan.
authorize Approve or commit an act implying subsequent action by others.
consult Consider, asking advice or requesting opinion of.
control Direct, regulate, or guide the use of money, methods, equipment, and materials. Also, the process of monitoring activities to ensure conformance with planned results.
coordinate Regulate, adjust or direct the related actions of others in order to attain desired results.
decide To select a course of action.
delegate Entrust to another person tasks or duties which require exercise of some of the authority of the person originally responsible, as “To delegate an administrative assistant to represent the department at conferences.”
determine To fix conclusively, regulate. To decide by choice of alternatives.
direct Govern or control work operations by establishing the implementing objectives, practices and methods.
enforce To effect or gain by force. To carry out effectively.
establish To institute permanently by enactment or agreement.
execute Put into effect or carry out methods, plans, etc.
initiate Set going or introduce.
manage Plan, organize, direct, control, and evaluate operation of an organizational unit, with responsibility for the output.
order Arrange or command to come to a specified place or decision.
organize To set up an administrative structure for. To arrange by systematic planning and united effort.
plan To design or plot a scheme or project by means or method devised for doing something to achieve an end.
reject To refuse to accept, consider or submit to.
require To ask for by right and authority, request.
review Consider or examine facts or results for accuracy, completeness and suitability.
supervise Personally oversee or control work performance and conduct of others, where there is opportunity for control or inspection of work performed.
train Teach, demonstrate, or guide others in the performance of assigned work.
   

PUSH ALONG VERBS

activate Set up or formally introduce with necessary personnel or equipment.
encourage Give help, inspire or pay patronage to.
expediate Accelerate the process or progress of a plan, idea.
further Promote or advance.
implement Carry out or fulfill by taking action.
maintain Keep in satisfactory condition.
motivate Provide incentive or drive.
   

STOP VERBS

check To proof or review for errors.
delete Eliminate or wipe out.
prevent Keep from happening or holding back.
return Go back in thought or action. Give an official account to a superior.
stop Keep from carrying out a proposed action.
   

HELPER VERBS

advise Offer an informed opinion or give specialized information to others.
aid Provide with what is useful or necessary for achieving an end.
cooperate Act jointly with others. Act or work with others to obtain a mutual benefit.
counsel Advise or consult.
explain Make plain or understandable.
guide Direct, supervise, influence or superintend the training of people.
instruct Teach, demonstrate, or by other methods impart knowledge to others. Direct that a specific activity be performed, may include directing how it is to be performed.
participate To take part or have a share in a project, group.
protect Maintain status or integrity of project, idea.
serve Comply with the commands and demands of a boss, group.
show Propose or mention an idea as workable or desirable.
suggest ?
   

GET & GIVE VERBS

accept Give admittance or approval to.
accumulate Increase gradually in quantity or number.
acquire Come into possession or control of an item or items.
arrange for To make preparations for, to plan.
buy Acquire possession, ownership or rights to the use of services, items.
collect Gather or exact information or materials from a number of persons or sources.
compile Put together information or assemble data in a new form.
deliver Send or bring a desired object.
distribute Deliver or hand out to several or many.
exchange Give and receive reciprocally.
forward Send goods or information onward.
furnish Provide or equip with what is needed.
gather Bring together or collect parts of a group.
get Obtain or receive.
give Grant or yield to another.
inform Communicate knowledge to others.
inquire Ask or search into.
issue Make available through distribution.
keep Preserve or maintain in a good and orderly condition.
mail To send by the postal service.
notify Give notice or a report on an occurrence or information.
obtain Gain or possess.
pick up ?
procure Get possession or obtain by particular care and effort.
provide To supply support to meet a need, make available.
pull Demand or obtain advantage by use of exertion or influence.
purchase Gain or acquire by labor, money.
recall Call back or cancel.
receive Come into possession of or acquire an item, idea.
recruit Increase numbers of a group or bring in new members.
render Deliver or hand down.
report Give an account or make a written summary or statement.
secure Put beyond hazard or receive lasting control.
sell Give up property in exchange for money.
send Deliver or dispatch as means of communication or delivery.
solicit To make a petition or request for services, money.
submit Yield or surrender to authority.
supply Make materials available for use.
take Get or seize into possession.
transfer Pass over from one person to another.
withdraw Back away or remove.
   

CREATIVE VERBS

create Produce through imaginative skill.
design Create or fashion a plan or idea.
develop Disclose, discover, perfect, or unfold a plan or idea, in detail, gradually. Implies study and/or experiment unless otherwise stated. When used as “to develop subordinates”, see train.
devise Form in the mind by combinations of ideas, new applications of principles, or new arrangement of parts.
establish To institute permanently by enactment or agreement.
estimate Forecast future quantities, values, sizes, extents, etc., either on the basis of judgment or calculations. Frequently, estimating is shared with others, in which case it is more precise to use “estimate” as a noun, and to state the job’s function in relation thereto, i.e., originates, analyzes, endorses, approves, etc., estimates of…
forecast Predict future events based on specified assumptions.
formulate Put into a systemized expression or statement.
iniyiate Set going or introduce.
install To set up for use.
originate Begin or initiate.
plan To design or plot a scheme or project by means or method devised for doing something to achieve an end.
project Plan, figure, or estimate for the future.
schedule Appoint a fixed time.
   

APPRAISE/STUDY VERBS

analyze Identify the elements of a whole and critically examine and relate these component parts separately and/or in relation to the whole.
appraise Judge as to quality; compare critically with established standards.
ascertain Find out or learn with certainty.
check To proof or review for errors.
compare To examine characteristics to discover similarities or differences.
consider To observe or think about with regard to taking some action.
criticize To evaluate and judge merits or faults.
develop Disclose, discover, perfect, or unfold a plan or idea, in detail, gradually. Implies study and/or experiment unless otherwise stated. When used as “to develop subordinates”, see train.
evaluate Appraise, to determine value, condition, significance or worth.
examine Investigate in order to determine progress, fitness or knowledge.
forecast Predict future events based on specified assumptions.
identify The act of proving identity.
inspect Examine materials, equipment, reports, work, etc., to determine quality, suitability for use, etc.
interpret Explain to others (orally or in writing) the meaning or significance of something.
interview Obtain information through questioning.
investigate Uncover facts by systematically finding them, conducting a search, and examining various sources.
measure Control or regulate by a standard or in measured amounts.
plan To design or plot a scheme or project by means or method devised for doing something to achieve an end.
rate Estimate or determine the relative value, rank, or amount of an item.
research Specific inquiry involving prolonged and critical investigation, having for its aim the study of new facts and their interpretation, the revision of accepted conclusions or theories that may be affected by newly discovered factors, or the practical application of such new or revised conclusions. Example: Technical research to develop new products for the company.
resolve Deal with a problem, dilemma successfully.
review Consider or examine facts or results for accuracy, completeness and suitability.
solve Find a solution, answer, or explanation for a question or problem.
study Apply thought to any subject of investigation in order to arrive at the most suitable conclusion.
summarize To tell and reduce a story, idea.
survey Examine a condition, situation or value.
test Assign a value or evaluate an item by a given test.
weigh Merit consideration as to importance.
   

CONTROL VERBS

allocate Assign or apportion for a specific purpose or to a particular person.
audit Perform a formal examination into a company’s formal accounts.
check To proof or review for errors.
conserve Slow or block the progress of something
control Direct, regulate, or guide the use of money, methods, equipment, and materials. Also, the process of monitoring activities to ensure conformance with planned results.
edit Alter, adapt or refine a written text, concept, or idea.
enforce To effect or gain by force. To carry out effectively.
ensure Make sure, certain, or safe.
guarantee Undertake to answer for debt and default or promise security.
inspect Examine materials, equipment, reports, work, etc., to determine quality, suitability for use, etc.
regulate Fix or adjust the time, amount, degree, or rate.
restrict Place under restriction as to use or distribution.
review Consider or examine facts or results for accuracy, completeness and suitability.
verify Confirm or substantiate by oath, law, or other documentation.

back to top


Trustworthiness

Just saw this demo of an algorithm that rates the trustworthiness of Wikipedia articles. It computes the “trust” of authors by how long their edits stick around, then applies that to articles. (via Slashdot)

I wonder how it works on entries that are intentionally misleading, like the entry on Discordianism.



More thoughts on an interesting thesaurus

My associate, Rebecca, and I have been starting to think critically about Panlexicon.com, the unique, tag-cloud based thesaurus I’ve written about previously. We’re hoping to put some more time and effort into the project and in the process, learn some more about what’s happening with the language and the underlying structure of the thesaurus taxonomy.

Panlexicon.com - Thesaurus Visualization

The thesaurus data we’re working with is the Moby Thesaurus from the Project Gutenburg library of free electronic texts. Like many thesauruses, it’s structure in an interesting way. Every word is assigned to one or more groups based on it’s general meaning or idea. Each group has a keyword, also known as a headword, that is a general encapsulation that idea—this is why, for example in Roget’s, you must first look up a word in the index to acquire its keywords. Each group has only one keyword, but a keyword can exist in other groups (but as an ordinary word).

This thesaurus structure allows us to do some easy simplifications and analysis on the data. For many functions, we can treat the groups as supernodes, performing operations and storing connections upon them in place of the words themselves. For example, when determining relatedness between words, we only have compare the groups they are a part of; while there are approximately 100,000 words in our database, there are only 30,000 groups, which greatly diminishes the size and complexity of the data set we’re working on.

Panlexicon.com - Correspondence Weighting

Currently Panlexicon works by comparing the overlap between groups of words. When typing in a search term, Panlexicon looks up all of the groups that word is a member of. It then returns a list of words that are also in those groups. The weight of each word (or size in our word cloud model) is calculated according to how many groups—-of those groups that include the search term—that word is a member of. A property of this is that no other returned word will have a heavier weight than the search term. When searching multiple terms, Panlexicon creates a set of groups such that all search terms are a member. In the case when there exists no groups that contain all the search terms, Panlexicon returns nothing.

Already we’re digging into some interesting relations that turn up in the thesaurus data. For example, one of my favorite linguistic myths is that Eskimos have 50 different words for snow. The supposed lesson was that eskimos had a different conception of snow than us (the non-Eskimos). I always wondered, “Well, is 50 a lot?” The largest group in our thesaurus has the keyword cut with 1448 related words or synonyms. This is followed by set (1152), turn (1108), run (1025), and color (1007). That’s quite a bit.

Also, interestingly in our dataset, are the most versatile words. These words are members of the most groups. The list shares four out five of the same words as those of the most synonyms, beginning with cut, being a member of 1120 distinct groups. This is followed by set (928), run (750), turn (715), and check (699).

Right now, we’re investigating paths between words. This will allow us to play the Kevin Bacon game, making connections between words that may not share the same group. It will be interesting to determine what words are connected (even through a medium) and which ones are disconnected. Lastly on our list of things to do is determine the eigenvectors of our groups in relation to how their connected to other groups. This will allow us to determine—without using fancy words like Markov chains—which words are probably used the most. I say probably because we’re analyzing a taxonomic work, rather than actual speech. Who knows if they match up; we’ll find out.


Two stories on a theme

I recently had an idea for a story and upon going through my other drafts, realized I already addressed it, though differently. So here they both are. Also, the second story is not a criticism of the Buddha—though if you asked me, “Surgeon General” is a more apt description than “Great Physician”—or Jesus for that matter (or anyone else that calls themselves a physician).

The Disciples

A wanderer entered a town to find a most pious people living there. In the market center he found a man in simple dress surrounded by disciples.

“Who is this man that attracts such a following?” the wanderer asked.

“This is our master, a most wise and blessed man. He has righted many people and teaches us his ways. Please, eat with us tonight and you will see but one of the miracles he can bestow.”

That evening the wanderer came to share their meal. He and the disciples waited solemnly with empty plates and glasses for their master to appear.

When the master arrived he but touched each plate and on it appeared food of the most delicious and nourishing variety; he placed his finger to a glass and it filled with the most refreshing of nectars.

“This is no trick,” a disciple said to the stranger. “It is evidence of the wondrous knowledge from which he teaches us.”

“I accept the miracle,” the wanderer replied, “But why must your master serve you?”

Midwife

The death of the king’s spiritual adviser called for the grandest of ceremonies and the richest of processions. To match the respect given in life, the king called all of his subjects in number to mourn. Among them, the king noticed a man, in the simple garb of a sage, and surrounded by disciples.

“Bring that wise man to my court this evening,” the king said, and it was so.

That evening, the sage was brought before the king and introduced as a most learned and wise teacher of philosophy and faith.

“Of my spiritual adviser,” the king spoke, “He was a brilliant and enlightened man whose help and support was a great constancy to my rule. Day or night I could call upon him to minister to my needs. He was physician to my thoughts and spirits.

“What could you provide of me in my court?” the king asked.

“To the health of your mind and soul I could provide much. I am though but a midwife: I can aid you in becoming, but from there it is up to you.”


DESC Scripting and Conflict Resolution

I’ve recently come into a major upgrade in duties at my job, which has always been heavy on the Human Resources responsibilities. As it is, not only am I supervising several subordinates locally, I am also the clearing house for supervisory advice and information for our many-dozen affiliates all across the nation; like the surgeon general is to doctors. Because of that, I find myself often intently reading HR manuals, strategic planning frameworks and, like today, conflict resolution.

Today I was leafing through the relatively good “How to Be a Better Supervisor” that was produced by the National Crime Prevention Council (the McGruff people) specifically for AmeriCorps (which is convenient considering that’s what I primarily deal with). I was clued into the publication by my friend and fellow nonprofit technologist Felicia Sullivan.

Most of this stuff is Communications 101. In other words, it’s that type of common sense that isn’t as common as you’d like it to be. Beyond that, and perhaps more importantly, it creates a framework and vocabulary for describing common sense in order for it to be more easily remembered, analyzed and communicated.

So the nice piece of common sense I ran across today was DESC Scripting, which was developed by Sharon and Gordon Bower.

One of many, many acronyms, DESC Scripting is a four step approach used to effect a change in behavior. I’ve also seen it called the DESC Model. It is (somewhat paraphrased):

  1. Describe the actions or behavior that you see as taking place;

  2. Express why that behavior is an issue?

  3. Specify the resulting actions or change of behavior you would like to effect;

  4. Clarify the consequences for failing to change behavior or meet demands.

It’s a rather good model for dealing with issues as they come up, mostly because it isn’t overly complicated or far outside the norm of typical interactions—no one could claim you didn’t deal with an issue.

Not to be meta about conflict resolution, I do have a critique of it: as acronyms go, it’s pretty lousy. The four acronym forming words have very similar definitions and in fact the meaning of the statement is defined in the words that follow—not merely expanded upon. In light of that, I propose the IRCC script:

  1. Issue: Describe the actions or behavior that you see are taking place.

  2. Result: Why are those actions or that behavior an issue?

  3. Correction: What are the resulting actions or change of behavior you would like to effect?

  4. Consequences: What happens if the actions or behavior changes aren’t met?

Of course, the issue with that is the double Cs at the end, but I’m open to suggestions (maybe later I’ll get into Conflict Resolution styles.)

More Info:

A BBC’s h2g2 has a good write-up on DESC Scripting and techniques for being more assertive.


Geekout: Video on Maps for Cable Access TV

I recently did some Drupal development work for Cambridge Community Television. As part of the really amazing work they are doing combining new media with traditional Cable Access Television, CCTV has been mapping videos their members produce. They call this project the Mediamap.

I was really excited to work on the Mediamap with CCTV because of my long involvement with Cable Access Television, most notably the now-defunct DigitalBicycle Project and the community maintained directory of Cable Access Stations I built and administer: MappingAccess.com.

Despite CCTV running their website on Drupal, their first proof-of-concept version of the Mediamap was created manually, using the very capable Mapbuilder.net service and copy-and-pasted embedded flash video. While simple from a technological standpoint, they were running to problems optimizing the workflow of updating the map; changes had to be made via the Mapbuilder.net interface, with a single username and password, then manually parsed to remove some coding irregularities, and finally copy and pasted whole into a page on their website.

I was asked to improve the workflow and ultimately take fuller advantage of Drupal’s built-in user management and content management features. For instance, taking advantage of CCTV’s current member submitted video capabilities and flowing them into the map as an integrated report, not a separate and parallel system.

In my discussions with them, a couple of issues came up. Foremost was that CCTV was running an older version of Drupal: 4.7. While still quite powerful, many newer features and contributed modules were not available for this earlier release. The current version of Drupal, 5.1, has many rich, well-developed utilities for creating reports and mapping them: Content Construction Kit (CCK) + Views + Gmap + Location. As it was though, with the older version, I would have to develop the additional functionality manually.

The following is a description, with code examples, of the functionality I created for the Mediamap. Additionally, following this initial development, CCTV upgraded their Drupal installation to 5.1, giving me the opportunity to demonstrate the ease and power of Drupal’s most recent release—rendering blissfully obsolete most of the custom coding I had done.

Location and Gmap was used in both versions for storing geographic data and hooking into the Google Map API. One of Drupal’s great strengths is the both the diversity of contributed modules, and the flexibility with which a developer can use them.

Adding additional content fields

CCTV already has a process in which member’s can submit content nodes. In 4.7, the easiest way to add additional data fields to these was with a custom NodeAPI module. CCTV was interested in using embedded flash video, primarily from Blip.tv, but also Google Video or YouTube if the flexibility was needed. To simplify the process, we decided on just adding the cut-and-paste embed code to a custom content field in existing nodes.

To do this, I created a new module that invoked hook_nodeapi:

/\*\*\ * Implementation of hook_nodeapi\ * /

function cambridge_mediamap_nodeapi(&$node, $op, $teaser, $page) {
  switch ($op) {
    case 'validate':
      if (variable_get('cambridge_mediamap_'.$node - > type, TRUE)) {
        if (user_access('modify node data')) {
          if ($node - > cambridge_mediamap['display'] && $node - > cambridge_mediamap['embed'] == '') {
            form_set_error('cambridge_mediamap', t('Media Map: You must enter embed code or disable display of this node on the map'));
          }
        }
      }
      break;
    case 'load':
      $object = db_fetch_object(db_query('SELECT display, embed FROM {cambridge_mediamap} WHERE nid = %d', $node - > nid));
      $embed = $object - > embed;
      $embed_resize = cambridge_mediamap_resize($embed);
      return array('cambridge_mediamap' => array('display' => $object - > display, 'embed' => $embed, 'embed_resize' => $embed_resize, ));
      break;
    case 'insert':
      db_query("INSERT INTO {cambridge_mediamap} (nid, display, embed) VALUES (%d, %d, '%s')", $node - > nid, $node - > cambridge_mediamap['display'], $node - > cambridge_mediamap['embed']);
      break;
    case 'update':
      db_query('DELETE FROM {cambridge_mediamap} WHERE nid = %d', $node - > nid);
      db_query("INSERT INTO {cambridge_mediamap} (nid, display, embed) VALUES (%d, %d, '%s')", $node - > nid, $node - > cambridge_mediamap['display'], $node - > cambridge_mediamap['embed']);
      break;
    case 'delete':
      db_query('DELETE FROM {cambridge_mediamap} WHERE nid = %d', $node - > nid);
      break;  
    case 'view':
      break;
  }
}

As you can see, there is a considerable amount of coding required, from defining the form, validating input and configuring database storage and retrieval calls.

Now that we have the glue for the custom field, we have to configure what node types that custom field appears on. Additionally, we need to set up administrative settings to configure where that custom field will appear, and lastly insert that field into the node edit screen:


\**
 * Implementation of hook_form_alter
 */
function cambridge_mediamap_nodeapi( & $node, $op, $teaser, $page) {
  switch ($op) {
    case 'validate':
      if (variable_get('cambridge_mediamap_'.$node - > type, TRUE)) {
        if (user_access('modify node data')) {
          if ($node - > cambridge_mediamap['display'] && $node - > cambridge_mediamap['embed'] == '') {
            form_set_error('cambridge_mediamap', t('Media Map: You must enter embed code or disable display of this node on the map'));
          }
        }
      }
      break;
    case 'load':
      $object = db_fetch_object(db_query('SELECT display, embed FROM {cambridge_mediamap} WHERE nid = %d', $node - > nid));
      $embed = $object - > embed;
      $embed_resize = cambridge_mediamap_resize($embed);
      return array(
        'cambridge_mediamap' => array(
          'display' => $object - > display,
          'embed' => $embed,
          'embed_resize' => $embed_resize,
        )
      );
      break;
    case 'insert':
      db_query("INSERT INTO {cambridge_mediamap} (nid, display, embed) VALUES (%d, %d, '%s')", $node - > nid, $node - > cambridge_mediamap['display'], $node - > cambridge_mediamap['embed']);
      break;
    case 'update':
      db_query('DELETE FROM {cambridge_mediamap} WHERE nid = %d', $node - > nid);
      db_query("INSERT INTO {cambridge_mediamap} (nid, display, embed) VALUES (%d, %d, '%s')", $node - > nid, $node - > cambridge_mediamap['display'], $node - > cambridge_mediamap['embed']);
      break;
    case 'delete':
      db_query('DELETE FROM {cambridge_mediamap} WHERE nid = %d', $node - > nid);
      break;
    case 'view':
      break;
  }
}

As you can see, that’s a lot of lines of code for what we essentially can do, in Drupal 5.1 with CCK. CCK allows you, graphically through the Drupal web-interface, to create a new content field and add it to a node type; it takes about a minute.

Building the Map

The primary goal of rebuilding the Mediamap using native Drupal was workflow optimization: it was frustrating to submit information both within Drupal and then recreate it within Mapbuilder. In essence, the map should be just another report of Drupal content: you may have a short bulleted list of the top five articles, a paginated history with teasers and author information, or a full-blown map, but most importantly, all of it is flowing dynamically out of the Drupal database.

The Gmap module provides many powerful ways to integrate the Google Map API with Drupal. While Gmap for 4.7 provides a default map of content it would not provide the features or customizability we desired with the Mediamap. Instead, one of the most powerful ways to use Gmap is to hook directly into the module’s own API-like functions:

\**
 * A page callback to draw the map
 */

function cambridge_mediamap_map() {
  $output = '';
  //Collect the nodes to be displayed
  $results = db_query('SELECT embed, nid FROM {cambridge_mediamap} WHERE display = 1');
  //Initialize our marker array
  $markers = array();
  //check to see what modules are enabled
  $location_enabled = module_exist('location');
  $gmap_location_enabled = module_exist('gmap_location');
  //load each node and set it's attributes in the marker array
  while ($item = db_fetch_object($results)) {
    $latitude = 0;
    $longitude = 0;
    //load the node
    $node = node_load(array('nid' => $item - > nid));
    //set the latitude and longitude
    //give location module data preference over gmap module data
    if ($location_enabled) {
      $latitude = $node - > location['latitude'];
      $longitude = $node - > location['longitude'];
    }
    elseif($gmap_location_enabled) {
      $latitude = $node - > gmap_location_latitude;
      $longitude = $node - > gmap_location_longitude;
    }
    if ($latitude && $longitude) {
      $markers[] = array(
        'label' => theme('cambridge_mediamap_marker', $node),
        'latitude' => $latitude,
        'longitude' => $longitude,
        'markername' => variable_get('cambridge_mediamap_default_marker', 'marker'),
      );
    }
  }
  $latlon = explode(',', variable_get('cambridge_mediamap_default_latlong', '42.369452,-71.100426'));
  $map = array(
    'id' => 'cambridge_mediamap',
    'latitude' => trim($latlon[0]),
    'longitude' => trim($latlon[1]),
    'width' => variable_get('cambridge_mediamap_default_width', '100%'),
    'height' => variable_get('cambridge_mediamap_default_height', '500px'),
    'zoom' => variable_get('cambridge_mediamap_default_zoom', 13),
    'control' => variable_get('cambridge_mediamap_default_control', 'Large'),
    'type' => variable_get('cambridge_mediamap_default_type', 'Satellite'),
    'markers' => $markers,
  );
   
  return gmap_draw_map($map);
}

As you can see, this is quite complicated. Drupal 5.1 offers the powerful Views module, which allows one to define custom reports, once again graphically from the Drupal web-interface, in just a couple minutes of configuration. The gmap_views module, which ships with Gmap, allows one to add those custom reports to a Google Map, which is incredibly useful and renders obsolete much of the development work I did.

On displaying video in maps

In my discussions with CCTV, we felt it most pragmatic to use the embedded video code provided by video hosting services such as Blip.tv. While we could have used one of the Drupal video modules, we wanted the ability to host video offsite due to storage constraints. While I was concerned about the danger of code injection via minimally validated inputs, we felt that this would be of small danger because the content would be maintained by CCTV staff and select members.

The markers were themed using the embedded video field pulled from the Drupal database, along with the title and a snippet of the description, all linking back to the full content node.

/**
 * A theme function for our markers
 */
function theme_cambridge_mediamap_marker($node) {
  $output = '';
  $output. = '' . l($node->title, 'node / ' . $node->nid) . '';
  $output. = '' . $node->cambridge_mediamap[' . embed_resize '] . '';
  $output. = '';
  return $output;
}

With Drupal 5.1 and Views, we still had to override the standard marker themes, but this was simple and done through the standard methods.

One of the most helpful pieces was some code developed by Rebecca White, who I previously worked with on Panlexicon. She provided the critical pieces of code that parsed the embedded video code and resized it for display on small marker windows.

/**

\* Returns a resized embed code

\*/
function cambridge_mediamap_resize($embed = '') {
  if (!$embed) {
    return '';
  }
  list($width, $height) = cambridge_mediamap_get_embed_size($embed);
  //width/height ratio
  $width_to_height = $width / $height;
  $max_width = variable_get('cambridge_mediamap_embed_width', '320');
  $max_height = variable_get('cambridge_mediamap_embed_height', '240');
  //shrink down widths while maintaining proportion
  if ($width >= $height) {
    if ($width > $max_width) {
      $width = $max_width;
      $height = (1 / $width_to_height)\ * $width;
    }
    if ($height > $max_height) {
      $height = $max_height;
      $width = ($width_to_height)\ * $height;
    }
  } else {
    if ($height > $max_height) {
      $height = $max_height;
      $width = ($width_to_height)\ * $height;
    }
    if ($width > $max_width) {
      $width = $max_width;
      $height = (1 / $width_to_height)\ * $width;
    }
  }
  return cambridge_mediamap_set_embed_size($embed, intval($width), intval($height));
}
/\*\*\ * find out what size the embedded thing is\ * /

function cambridge_mediamap_get_embed_size($html) {
  preg_match('/]\*width(\s\*=\s\*"|:\s\*)(\d+)/i', $html, $match_width);
  preg_match('/]\*height(\s\*=\s\*"|:\s\*)(\d+)/i', $html, $match_height);
  return array($match_width[2], $match_height[2]);
}
/\*\*\ * set the size of the embeded thing\ * /

function cambridge_mediamap_set_embed_size($html, $width, $height) {
  $html = preg_replace('/(<(embed|object)\s[^>]\*width(\s\*=\s\*"|:\s\*))(\d+)/i', '${1}'.$width, $html);
  $html = preg_replace('/(<(embed|object)\s[^>]\*height(\s\*=\s\*"|:\s\*))(\d+)/i', '${1}'.$height, $html);
  return $html;
}
/\*\*\ * returns the base url of the src attribute.\*youtube = www.youtube.com\ * blip = blip.tv\ * google video = video.google.com\ * /

function cambridge_mediamap_get_embed_source($html) {
  preg_match('/]\*src="http:\/\/([^\/"]+)/i', $html, $match_src);
  return $match_src[1];
}

The Wrap-Up

While it may not seem so from the lines of code above, developing for Drupal is still relatively easy. Drupal provides a rich set of features for developers, well documented features, and strong coding standards—making reading other people’s code and learning from it incredibly productive.

Below is the entirety of the custom module I developed for the 4.7 version of the CCTV Media Map. Because it was custom and intended to be used in-house, many important, release worthy functions were omitted, such as richer administrative options and module/function verifications.

'cambridge_mediamap', 'title' => t('Mediamap'), 'callback' => 'cambridge_mediamap_map', 'access' => user_access('access mediamap'), );
}
return $items;
}
/\*\*\ * Implementation of hook_nodeapi\ * /

function cambridge_mediamap_nodeapi( & $node, $op, $teaser, $page) {
  switch ($op) {
    case 'validate':
      if (variable_get('cambridge_mediamap_'.$node - > type, TRUE)) {
        if (user_access('modify node data')) {
          if ($node - > cambridge_mediamap['display'] && $node - > cambridge_mediamap['embed'] == '') {
            form_set_error('cambridge_mediamap', t('Media Map: You must enter embed code or disable display of this node on the map'));
          }
        }
      }
      break;
    case 'load':
      $object = db_fetch_object(db_query('SELECT display, embed FROM {cambridge_mediamap} WHERE nid = %d', $node - > nid));
      $embed = $object - > embed;
      $embed_resize = cambridge_mediamap_resize($embed);
      return array('cambridge_mediamap' => array('display' => $object - > display, 'embed' => $embed, 'embed_resize' => $embed_resize, ));
      break;
    case 'insert':
      db_query("INSERT INTO {cambridge_mediamap} (nid, display, embed) VALUES (%d, %d, '%s')", $node - > nid, $node - > cambridge_mediamap['display'], $node - > cambridge_mediamap['embed']);
      break;
    case 'update':
      db_query('DELETE FROM {cambridge_mediamap} WHERE nid = %d', $node - > nid);
      db_query("INSERT INTO {cambridge_mediamap} (nid, display, embed) VALUES (%d, %d, '%s')", $node - > nid, $node - > cambridge_mediamap['display'], $node - > cambridge_mediamap['embed']);
      break;
    case 'delete':
      db_query('DELETE FROM {cambridge_mediamap} WHERE nid = %d', $node - > nid);
      break;
    case 'view':
      break;
  }
}
/\*\*\ * Returns a resized embed code\ * /

function cambridge_mediamap_resize($embed = '') {
  if (!$embed) {
    return '';
  }
  list($width, $height) = cambridge_mediamap_get_embed_size($embed);
  //width/height ratio
  $width_to_height = $width / $height;
  $max_width = variable_get('cambridge_mediamap_embed_width', '320');
  $max_height = variable_get('cambridge_mediamap_embed_height', '240');
  //shrink down widths while maintaining proportion
  if ($width >= $height) {
    if ($width > $max_width) {
      $width = $max_width;
      $height = (1 / $width_to_height)\ * $width;
    }
    if ($height > $max_height) {
      $height = $max_height;
      $width = ($width_to_height)\ * $height;
    }
  } else {
    if ($height > $max_height) {
      $height = $max_height;
      $width = ($width_to_height)\ * $height;
    }
    if ($width > $max_width) {
      $width = $max_width;
      $height = (1 / $width_to_height)\ * $width;
    }
  }
  return cambridge_mediamap_set_embed_size($embed, intval($width), intval($height));
}
/\*\*\ * find out what size the embedded thing is\ * /

function cambridge_mediamap_get_embed_size($html) {
  preg_match('/]\*width(\s\*=\s\*"|:\s\*)(\d+)/i', $html, $match_width);
  preg_match('/]\*height(\s\*=\s\*"|:\s\*)(\d+)/i', $html, $match_height);
  return array($match_width[2], $match_height[2]);
}
/\*\*\ * set the size of the embeded thing\ * /

function cambridge_mediamap_set_embed_size($html, $width, $height) {
  $html = preg_replace('/(<(embed|object)\s[^>]\*width(\s\*=\s\*"|:\s\*))(\d+)/i', '${1}'.$width, $html);
  $html = preg_replace('/(<(embed|object)\s[^>]\*height(\s\*=\s\*"|:\s\*))(\d+)/i', '${1}'.$height, $html);
  return $html;
}
/\*\*\ * returns the base url of the src attribute.\*youtube = www.youtube.com\ * blip = blip.tv\ * google video = video.google.com\ * /

function cambridge_mediamap_get_embed_source($html) {
  preg_match('/]\*src="http:\/\/([^\/"]+)/i', $html, $match_src);
  return $match_src[1];
}
/\*\*\ * Implementation of hook_form_alter\ * /

function cambridge_mediamap_form_alter($form_id, & $form) {
  // We're only modifying node forms, if the type field isn't set we don't need
  // to bother.
  if (!isset($form['type'])) {
    return;
  }
  //disable the Gmap module's location map for unauthorized users
  //unfortunately Gmap.module doesn't have this setting
  if (isset($form['coordinates'])) {
    if (!user_access('modify node data')) {
      unset($form['coordinates']);
    }
  }
  // Make a copy of the type to shorten up the code
  $type = $form['type']['#value'];
  // Is the map enabled for this content type?
  $enabled = variable_get('cambridge_mediamap_'.$type, 0);
  switch ($form_id) {
    // We need to have a way for administrators to indicate which content
    // types should have the additional media map information added.
    case $type.
    '_node_settings':
      $form['workflow']['cambridge_mediamap_'.$type] = array('#type' => 'radios', '#title' => t('Cambridge Mediamap setting'), '#default_value' => $enabled, '#options' => array(0 => t('Disabled'), 1 => t('Enabled')), '#description' => t('Allow the attaching of externally hosted imbedded video to be displayed in a map?'), );
      break;
    case $type.
    '_node_form':
      if ($enabled && user_access('modify node data')) {
        //create the fieldset
        $form['cambridge_mediamap'] = array('#type' => 'fieldset', '#title' => t('Media Map'), '#collapsible' => TRUE, '#collapsed' => FALSE, '#tree' => TRUE, );
        //insert the embed code
        $form['cambridge_mediamap']['embed'] = array('#type' => 'textarea', '#title' => t('Video Embed Code'), '#default_value' => $form['#node'] - > cambridge_mediamap['embed'], '#cols' => 60, '#rows' => 5, '#description' => t('Copy and paste the embed code from an external video or media hosting service'), );
        //enable or disable on map
        $form['cambridge_mediamap']['display'] = array('#type' => 'select', '#title' => t('Display this node'), '#default_value' => $form['#node'] - > cambridge_mediamap['display'], '#options' => array('0' => t('Disable display'), '1' => t('Enable display'), ), );
      }
      break;
  }
}
/\*\*\ * A page callback to draw the map\ * /

function cambridge_mediamap_map() {
  $output = '';
  //Collect the nodes to be displayed
  $results = db_query('SELECT embed, nid FROM {cambridge_mediamap} WHERE display = 1');
  //Initialize our marker array
  $markers = array();
  //check to see what modules are enabled
  $location_enabled = module_exist('location');
  $gmap_location_enabled = module_exist('gmap_location');
  //load each node and set it's attributes in the marker array
  while ($item = db_fetch_object($results)) {
    $latitude = 0;
    $longitude = 0;
    //load the node
    $node = node_load(array('nid' => $item - > nid));
    //set the latitude and longitude
    //give location module data preference over gmap module data
    if ($location_enabled) {
      $latitude = $node - > location['latitude'];
      $longitude = $node - > location['longitude'];
    }
    elseif($gmap_location_enabled) {
      $latitude = $node - > gmap_location_latitude;
      $longitude = $node - > gmap_location_longitude;
    }
    if ($latitude && $longitude) {
      $markers[] = array('label' => theme('cambridge_mediamap_marker', $node), 'latitude' => $latitude, 'longitude' => $longitude, 'markername' => variable_get('cambridge_mediamap_default_marker', 'marker'), );
    }
  }
  $latlon = explode(',', variable_get('cambridge_mediamap_default_latlong', '42.369452,-71.100426'));
  $map = array('id' => 'cambridge_mediamap', 'latitude' => trim($latlon[0]), 'longitude' => trim($latlon[1]), 'width' => variable_get('cambridge_mediamap_default_width', '100%'), 'height' => variable_get('cambridge_mediamap_default_height', '500px'), 'zoom' => variable_get('cambridge_mediamap_default_zoom', 13), 'control' => variable_get('cambridge_mediamap_default_control', 'Large'), 'type' => variable_get('cambridge_mediamap_default_type', 'Satellite'), 'markers' => $markers, );
  return gmap_draw_map($map);
}
/\*\*\ * A theme
function
for our markers\ * /

function theme_cambridge_mediamap_marker($node) {
  $output = '
  ';
  $output. = '
  ' . l($node->title, '
  node / ' . $node->nid) . '
  ';
  $output. = '
  ' . $node->cambridge_mediamap['
  embed_resize '] . '
  ';
  $output. = '
  ';
  return $output;
}
/\*\*\ * Settings page\ * /

function cambridge_mediamap_settings() {
  // Cambridge data
  // latitude = 42.369452
  // longitude = -71.100426
  $form['defaults'] = array('#type' => 'fieldset', '#title' => t('Default map settings'), );
  $form['defaults']['cambridge_mediamap_default_width'] = array('#type' => 'textfield', '#title' => t('Default width'), '#default_value' => variable_get('cambridge_mediamap_default_width', '100%'), '#size' => 25, '#maxlength' => 6, '#description' => t('The default width of a Google map. Either px or %'), );
  $form['defaults']['cambridge_mediamap_default_height'] = array('#type' => 'textfield', '#title' => t('Default height'), '#default_value' => variable_get('cambridge_mediamap_default_height', '500px'), '#size' => 25, '#maxlength' => 6, '#description' => t('The default height of Mediamap. In px.'), );
  $form['defaults']['cambridge_mediamap_default_latlong'] = array('#type' => 'textfield', '#title' => t('Default center'), '#default_value' => variable_get('cambridge_mediamap_default_latlong', '42.369452,-71.100426'), '#description' => 'The decimal latitude,longitude of the centre of the map. The "." is used for decimal, and "," is used to separate latitude and longitude.', '#size' => 50, '#maxlength' => 255, '#description' => t('The default longitude, latitude of Mediamap.'), );
  $form['defaults']['cambridge_mediamap_default_zoom'] = array('#type' => 'select', '#title' => t('Default zoom'), '#default_value' => variable_get('cambridge_mediamap_default_zoom', 13), '#options' => drupal_map_assoc(range(0, 17)), '#description' => t('The default zoom level of Mediamap.'), );
  $form['defaults']['cambridge_mediamap_default_control'] = array('#type' => 'select', '#title' => t('Default control type'), '#default_value' => variable_get('cambridge_mediamap_default_control', 'Large'), '#options' => array('None' => t('None'), 'Small' => t('Small'), 'Large' => t('Large')), );
  $form['defaults']['cambridge_mediamap_default_type'] = array('#type' => 'select', '#title' => t('Default map type'), '#default_value' => variable_get('cambridge_mediamap_default_type', 'Satellite'), '#options' => array('Map' => t('Map'), 'Satellite' => t('Satellite'), 'Hybrid' => t('Hybrid')), );
  $markers = gmap_get_markers();
  $form['defaults']['cambridge_mediamap_default_marker'] = array('#type' => 'select', '#title' => t('Marker'), '#default_value' => variable_get('cambridge_mediamap_default_marker', 'marker'), '#options' => $markers, );
  $form['embed'] = array('#type' => 'fieldset', '#title' => t('Default embedded video settings'), );
  $form['embed']['cambridge_mediamap_embed_width'] = array('#type' => 'textfield', '#title' => t('Default width'), '#default_value' => variable_get('cambridge_mediamap_embed_width', '320'), '#size' => 25, '#maxlength' => 6, '#description' => t('The maximum width of embedded video'), );
  $form['embed']['cambridge_mediamap_embed_height'] = array('#type' => 'textfield', '#title' => t('Default height'), '#default_value' => variable_get('cambridge_mediamap_embed_height', '240'), '#size' => 25, '#maxlength' => 6, '#description' => t('The maximum height of embedded video.'), );
  return $form;
}
/\*\*\ * Prints human - readable(html) information about a variable.\*Use: print debug($variable_name);\ * Or assign output to a variable.\*/

function debug($value) {
  return preg_replace("/\s/", " ", preg_replace("/\n/", "", print_r($value, true)));
}