Map and Image Annotator NetLogo Model

Produced for the book series "Artificial Intelligence";

Author: W. J. Teahan; Publisher: Ventus Publishing Aps, Denmark.

powered by NetLogo

view/download model file: Map-and-Image-Annotator.nlogo

WHAT IS IT?

This model allows the user to annotate a map or an image. The model imports an image into the background for the environment. The user can then add arrows, circles, text and marks (i.e. x's). These can be later edited or erased if required.


HOW IT WORKS

The map or image is placed in the background using the import-drawing command. Separate turtle agent breeds - arrow, circle, text and mark - are used for drawing the corresponding object in the environment. A further side turtle agent is used for drawing the sides of the rectangular box used by the select-region and erase-region for selecting and erasing multiple objects.

Arrows are drawn using two arrow turtle agents. The start arrow turtle agent is given a circle shape, and the end arrow turtle agent is drawn using NetLogo's default arrowhead shape. The shaft of the arrow is drawn by creating a link between the two arrow turtle agents. Circles and marks are standard turtle agents with a specific size, colour and thickness drawn at a specific location using the mouse. Text is drawn by setting the label of turtle agents that are drawn with size 0 - i.e. only the label of the agent is made visible, not the agent itself.

NetLogo's distancxy reporter is used to find the nearest object by the select-object and erase-object procedures. The watch command is used to highlight the selected object. The global variable selected is used by the select-region and erase-region procedures to hold an agentset of objects that are within the specified region. The entire agentset can then easily be dragged together or erased in unison using a single ask command.


HOW TO USE IT

Pressing setup clears the environment and then loads the image as specified by the map-or-image-to-use chooser. Then the user can place arrows, circles, text and marks over the top of the image using the draw-arrow, draw-circle, draw-text and draw-mark buttons by pointing then clicking using the mouse.


THE INTERFACE

The buttons in the Interface are defined as follows:

- setup: This clears the environment and loads a new image as specified by the map-or-image-to-use chooser

- draw-arrow: This will draw an arrow over the top of the image. If the user then clicks and drags the mouse, an arrow is drawn with the tail at the point the mouse was clicked, and the head at the point the mouse was released. (The mouse must be both clicked and dragged, otherwise no arrow is drawn). The colour and dimensions of the arrow are specified by the arrow-colour, arrow-start-size, arrow-end-size and arrow-thickness sliders. To get just a single arrow head shape without the shaft and arrow tail, then use the erase-object button to erase the end of the arrow. (This can also be used to erase the arrow head and leave just the tail if so desired).

- draw-circle: This will draw a circle over the top of the image at the point the mouse is next clicked. The colour and dimensions of the circle are specified by the circle-colour, circle-size and circle-thickness sliders.

- draw-text: This will draw text over the top of the image at the point the mouse is next clicked. Note that the right side of the text will be set to the mouse click point (rather than the left). The content of the text is specified by the text-to-insert input box. To update this, the user must click inside the input box, then edit the existing text or clear it and replace it with new text. The text update is only completed when the user types the return key. The colour of the text is specified by the text-colour slider.

- draw-mark: This will draw a mark over the top of the image at the point the mouse is next clicked.

- select-object: This will select an object if it is near to the next mouse click point. The object will have a halo placed around it to show when it has been selected. Subsequently dragging the mouse will result in the selected object being dragged around. An object is an arrow head or tail, a circle, text or a mark. When dragging the head or tail of an arrow, the other end will remain stationary (i.e. the arrow will pivot around the other end).

- erase-object: This will erase an object if it is near to the next mouse click point. If an arrow head or tail is erased, then the shaft is erased as well, and only the other end will remain.

- select-region: This allows multiple objects to be selected. Click using the mouse then drag. A rectangular box will delimit the region of objects that are selected. Temporarily stop clicking the mouse, then click back somewhere inside the rectangular box, then drag it and everything inside it using the mouse. All the objects inside the box will move to the point where the mouse is dragged to.

The Interface's choosers, sliders and input box are defined as follows:

- map-or-image-to-use: This specifies which image gets loaded into the environment. If set to "User-specified", then the user will be prompted for an image filename. (This will not work when the model is invoked in an applet).

- arrow-colour: This sets the colour of the arrow that is drawn when the draw-arrow button is next pressed. The colour is specified in a range from 0 to 140 using NetLogo's colour numbering scheme. Sample colour numbers are shown in the small text note at the left bottom of the Interface.

- arrow-start-size: This sets the size of the arrow tail (drawn as a filled-in circle).

- arrow-end-size: This sets the size of the arrow head (drawn using the default arrowhead shape).

- arrow-thickness: This sets the thickness of the arrow's shaft.

- circle-colour: This sets the colour of the circle that is drawn when the draw-circle button is next pressed. The colour is specified in a range from 0 to 140 using NetLogo's colour numbering scheme. Sample colour numbers are shown in the small text note at the left bottom of the Interface.

- circle-size: This sets the size of the circle.

- circle-thickness: This sets the thickness of the circle.

- text-to-insert: This sets the text that is drawn when the draw-text is next pressed. The text is right justified rather than left justified. i.e. The end of the text (not the start) will end up at the point where the mouse is clicked. The size of the text can be specified globally using the Settings button at the top of the Interface.

- text-colour: This sets the colour of the text that is drawn when the draw-text button is next pressed. The colour is specified in a range from 0 to 140 using NetLogo's colour numbering scheme. Sample colour numbers are shown in the small text note at the left bottom of the Interface.

- mark-colour: This sets the colour of the mark (i.e. a letter "x") that is drawn when the draw-mark button is next pressed. The colour is specified in a range from 0 to 140 using NetLogo's colour numbering scheme. Sample colour numbers are shown in the small text note at the left bottom of the Interface.

- mark-size: This sets the size of the mark.


THINGS TO NOTICE

Notice that some of the images do not fit the environment exactly (there are white unfilled areas above and below the image in the environment). This is because the import-drawing command maintains the same aspect ratio of the image (i.e. the width and height have the same ratio relative to each other as the image is scaled). The image, however, is sized to fit the environment, and therefore to keep the same aspect ratio, there will be some unfilled areas in the environment as a result.


THINGS TO TRY

Try using the model to annotate your own map or image, such as a photo you might have taken on holiday. Then use the print screen feature on your computer to save the annotated image.


EXTENDING THE MODEL

Try modifying the model so that it allows the user to draw lines and add further objects.


NETLOGO FEATURES

The code makes use of the watch, subject and perspective commands when the select-object button is used to place a halo around the selected object.


RELATED MODELS

See the Mouse Drag Multiple Example and Mouse Drag One Example models.


CREDITS AND REFERENCES

The model makes use of code provided by Uri Wilensky in the Mouse Drag Multiple Example and Mouse Drag One Example models.

This model was created by William John Teahan.

To refer to this model in publications, please use:

Map and Image Annotator NetLogo model.
Teahan, W. J. (2010). Artificial Intelligence. Ventus Publishing Aps


PROCEDURES

breed [ arrows arrow ]   ;; for drawing arrows
breed [ circles circle ] ;; for drawing circles
breed [ texts text ]     ;; for drawing text
breed [ marks mark ]     ;; for drawing marks (i.e. "X" marks the spot)
breed [ sides side ]     ;; the four sides of the selection square

globals
[
  selected               ;; agentset of currently selected objects
]

to setup

  ca ; clear everything

  ask patches
  [ set pcolor white ] ; make the background white
    
  setup-map-or-image
  ; ask turtles [ die ]
    
  set selected no-turtles
  set-default-shape sides "line"
end

to setup-map-or-image
  ifelse (map-or-image-to-use = "User-specified")
    [
      import-drawing user-file
    ]
    [
      let maps
        [
          "Sample map 1"   "Map-and-Image-Annotator-Sample-Map-1.jpg"
          "Sample map 2"   "Map-and-Image-Annotator-Sample-Map-2.jpg"
          "Sample map 3"   "Map-and-Image-Annotator-Sample-Map-3.png"
          "Sample map 4"   "Map-and-Image-Annotator-Sample-Map-4.png"
          "Sample image 1" "Map-and-Image-Annotator-Sample-image-1.jpg"
          "Sample image 2" "Map-and-Image-Annotator-Sample-image-2.jpg"
          "Sample image 3" "Map-and-Image-Annotator-Sample-image-3.jpg"
          "Sample image 4" "Map-and-Image-Annotator-Sample-image-4.jpg"
        ]

      let p position map-or-image-to-use maps
      let filename item (p + 1) maps
     
      import-drawing filename     
    ]
end

to draw-arrow
; Draws an arrow at the point the mouse has been pressed.

  let start-arrow-point nobody
  let end-arrow-point nobody
 
  if mouse-down?
  [
    create-arrows 1 ; create the starting point of the arrow
    [ set start-arrow-point self
      set size arrow-start-size
      set shape "circle"
      ; hide-turtle
      set color arrow-colour
      setxy mouse-xcor mouse-ycor ]
    while [mouse-down?] [ ] ; wait until mouse is lifted
    create-arrows 1 ; create the ending point of the arrow
    [ set end-arrow-point self
      set size arrow-end-size
      set color arrow-colour
      setxy mouse-xcor mouse-ycor
      ifelse (xcor = [xcor] of start-arrow-point) and
         (ycor = [ycor] of start-arrow-point)
        [ ; the head and the tail are in the same place - delete them
          ask start-arrow-point
          [ die ]
          die
        ]
        [ ; the head the tail are at different locations, so draw the arrow
          face start-arrow-point ; head opposite to direction to start point
          rt 180 ; face opposite direction
          create-link-with start-arrow-point
          [ set thickness arrow-thickness
            set color arrow-colour
          ]
        ]
    ]
 ]
 display
end

to create-circle
; Creates a circle agent for drawing circles.

  set shape "thin ring" ; this was drawn with the Turtle Shapes Editor
  set size circle-size
  set color circle-colour
  __set-line-thickness circle-thickness
  setxy mouse-xcor mouse-ycor
  while [mouse-down?]
  [
    display
    setxy mouse-xcor mouse-ycor
  ]
end

to draw-circle
; Draws a circle at the point the mouse has been pressed.

  if mouse-down?
  [
    create-circles 1 [ create-circle ]
  ]
  display
end

to create-text
; Creates a text agent for drawing text.

  set label-color text-colour
  set size 0
  set label text-to-insert
  setxy mouse-xcor mouse-ycor
  while [mouse-down?]
  [
    display
    setxy mouse-xcor mouse-ycor
  ]
end

to draw-text
; Draws text at the point the mouse has been pressed.

  if mouse-down?
  [
    create-texts 1 [ create-text ]
  ]
  display
end

to create-mark
; Creates a mark agent for drawing a mark (i.e. "X marks the spot".

  set color mark-colour
  set size mark-size
  set shape "x"
  setxy mouse-xcor mouse-ycor
  while [mouse-down?]
  [
    display
    setxy mouse-xcor mouse-ycor
  ]
end

to draw-mark
; Draws text at the point the mouse has been pressed.

  if mouse-down?
  [
    create-marks 1 [ create-mark ]
  ]
  display
end

to reorient-arrow-agents
; For ensuring that the arrowheads stay facing the same heading as the
; arrow shafts.

  if (shape != "circle") and (count link-neighbors > 0)
    [
      face one-of link-neighbors
      rt 180 ; face away
    ]
  if (shape = "circle") and (count link-neighbors > 0)
    [
      ask link-neighbors
      [ face myself
        rt 180 ; face away
      ]
    ]
end

to erase-object
  if mouse-down? [
    let candidate min-one-of turtles [distancexy mouse-xcor mouse-ycor]
    if (candidate != nobody)
      [
        if [distancexy mouse-xcor mouse-ycor] of candidate < 3
          [ ask candidate [ die ]]
      ]
    display
  ]
end

to select-object
;; selects an object on the map and "watches" it
  if mouse-down? [
    let candidate min-one-of turtles [distancexy mouse-xcor mouse-ycor]
    if [distancexy mouse-xcor mouse-ycor] of candidate < 3 [
      ;; The WATCH primitive puts a "halo" around the watched turtle.
      watch candidate
      while [mouse-down?]
      [
        ;; If we don't force the display to update, the user won't
        ;; be able to see the turtle moving around.
        display
        ;; The SUBJECT primitive reports the turtle being watched.
        ask subject
        [ setxy mouse-xcor mouse-ycor
          reorient-arrow-agents
        ]
      ]
      ;; Undoes the effects of WATCH.  Can be abbreviated RP.
      reset-perspective
    ]
  ]
end

to erase-region
;; erases the selected region
  ask selected [ die ]
  ask sides [ die ]
  display
  set selected no-turtles
  ;;handle-select
end

to select-region
  if mouse-down? [
    ifelse selected? mouse-xcor mouse-ycor
      [ handle-drag
        deselect ]
      [ handle-select ]
  ]
end

to handle-select
  ;; remember where the mouse pointer was located when
  ;; the user pressed the mouse button
  let old-x mouse-xcor
  let old-y mouse-ycor
  while [mouse-down?] [
    select old-x old-y mouse-xcor mouse-ycor
    ;; update the view, otherwise the user can't see
    ;; what's going on
    display
  ]
  ;; if no turtles are selected, kill off
  ;; the selection square and start over
  if not any? selected [ deselect ]
end

to handle-drag
  ;; remember where the mouse pointer was located when
  ;; the user pressed the mouse button
  let old-x mouse-xcor
  let old-y mouse-ycor
  if selected? old-x old-y [
    while [mouse-down?] [
      let new-x mouse-xcor
      let new-y mouse-ycor
      let new-x1 0
      let new-y1 0
      ;; we need to move both the selected turtles and the sides
      ;; of the selection square by the same amount that the
      ;; mouse has moved.  we do this by subtracting the current
      ;; mouse coordinates from the previous mouse coordinates
      ;; and adding the results to the coordinates of the turtles
      ;; and sides.
      ask selected
        [
          set new-x1 xcor + new-x - old-x
          set new-y1 ycor + new-y - old-y
          if (new-x1 >= min-pxcor) and (new-x1 <= max-pxcor) and
             (new-y1 >= min-pycor) and (new-y1 <= max-pycor)
            [
              setxy new-x1 new-y1
              reorient-arrow-agents
            ]
        ]
      ask sides
        [
          set new-x1 xcor + new-x - old-x
          set new-y1 ycor + new-y - old-y
          if (new-x1 >= min-pxcor) and (new-x1 <= max-pxcor) and
             (new-y1 >= min-pycor) and (new-y1 <= max-pycor)
            [ setxy new-x1 new-y1 ]
        ]
      set old-x new-x
      set old-y new-y
      ;; update the view, otherwise the user can't see
      ;; what's going on
      display
    ]
  ]
end

to deselect
  ask sides [ die ]
  set selected no-turtles
end

to select [x1 y1 x2 y2]   ;; x1 y1 is initial corner and x2 y2 is current corner
  deselect  ;; kill old selection square
  make-side x1 y1 x2 y1
  make-side x1 y1 x1 y2
  make-side x1 y2 x2 y2
  make-side x2 y1 x2 y2
  set selected turtles with [selected? xcor ycor]
end

to make-side [x1 y1 x2 y2]
  ;; for each side, one thin line shape is created at the mid point of each segment
  ;; of the bounding box and scaled to the proper length
  create-sides 1 [
    set color gray
    setxy (x1 + x2) / 2
          (y1 + y2) / 2
    facexy x1 y1
    set size 2 * distancexy x1 y1
  ]
end

;; helper procedure that determines whether a point is
;; inside the selection square
to-report selected? [x y]
  if not any? sides [ report false ]
  let y-max max [ycor] of sides   ;; largest ycor is where the top is
  let y-min min [ycor] of sides   ;; smallest ycor is where the bottom is
  let x-max max [xcor] of sides   ;; largest xcor is where the right side is
  let x-min min [xcor] of sides   ;; smallest xcor is where the left side is
  ;; report whether the input coordinates are within the square
  report x >= x-min and x <= x-max and
         y >= y-min and y <= y-max
end