Wednesday, June 28, 2017

Angular Carto: Custom Zoom Buttons

This is part of a series covering how to create reusable Carto map components. Read the introduction to learn why one would even want to do such a thing. Read the previous installment, Carto Map Factory, to learn about adding a Carto map to a webpage and registering the map with the Angular dependency injector.

In this post, we will discuss how to add custom zoom buttons to the map.

What are directives?

We will implement our custom zoom buttons by encapsulating them in an Angular directive. Directives are a technique for packaging up bundles of HTML content and JavaScript code. Once a directive has been registered in your Angular module, it can be interpolated into the HTML of your web page, most commonly as an HTML element or as an attribute on a standard element. A good use case for directives is a text field with a date picker. The date picker should pop up a window with a navigable calendar, allow the user to browse through the calendar to find the date they want, and then save the date in the text field attached to the date picker. Additionally, the ideal date picker should be reusable throughout a website with a minimum of effort on the part of the programmer.

If directives are a new concept to you, I recommend Sandeep Panda’s “A Practical Guide to AngularJS Directives.”

The zoom buttons directive

Registering the directive

Directives are added to Angular modules using the directive function. The first argument is the name under which we are registering the directive; the second argument is a factory function for creating instances of the directive. Note that the registered name of the directive, zoomButtons, is camel-cased.

Note that in this sample code, the directive factory is an anonymous function, but you can (and perhaps should!) define a named function in a site-wide library and register the named function wherever zoom buttons are needed. Note also that the directive factory takes a parameter named map. At run time, the Angular dependency injector will pass whatever is registered under the name “map” as an argument to the directive factory. For more information about registering a Carto map with the Angular dependency injector, please see my previous post “Map Factory.”

Restricting calling style

Angular provides several different ways of calling a directive from HTML. The two most common are element style (<zoom-buttons></zoom-buttons>) and attribute style (<div zoom-buttons></div>). All calling styles produce the same results, so the Angular team recommends choosing the calling style to signal how the directive is used.

When should I use an attribute versus an element? Use an element when you are creating a component that is in control of the template. The common case for this is when you are creating a Domain-Specific Language for parts of your template. Use an attribute when you are decorating an existing element with new functionality.
To allow those two calling styles, we specify A (for attribute-style) and E (for element-style) in the restrict property of the directive. There are other directive calling styles, but it is not necessary to discuss them here.

Writing HTML to be interpolated

Next, we need to define an HTML template for the content that will be interpolated into the web page when the directive is invoked.

Each directive has its own $scope. If you are not familiar with $scope, for the purposes of this article it is best understood as the view model for an Angular component; in this case, $scope is the view model for the zoom buttons directive. The ng-click data binding and double-brace string interpolation are bound to methods and properties on this directive’s $scope.

This directive defines two buttons for zooming in and out. When clicked, the buttons call, respectively, the zoomIn() and zoomOut() functions on the $scope. The labels on the buttons are likewise defined in the zoomInLabel and zoomOutLabel properties on the $scope. Finally, to ensure that the caller of the directive has control over the appearance of the buttons, their class and style attributes are bound to the buttonClass and buttonStyle properties of the $scope.

You can define your HTML template either using a string literal, or by pulling it into a separate file if it is too unwieldy to manage within a string literal. Beware: the sample code below shows both techniques, but you should pick one or the other.

Configuring the directive $scope

When configuring a directive, we define a link() function that configures the directive $scope. The link() function is takes information from the directive invocation in the web page and translates that into the $scope required by the HTML template.

The first parameter of the link() function is the $scope to be configured. The second parameter is the HTML element on which the directive was invoked. The third parameter is the attributes of said element. In this particular link() function, information from the attributes are used to set the label and styling properties on the $scope. Note that the $scope.zoom*() functions are set to be map.zoom*.bind(map). Why not just set them to map.zoom* instead? Because the map.zoom* functions contain a variety of references to this. If you use $scope.zoom* = map.zoom*, those this references will be treated as referring to $scope rather than map. Using $scope.zoom* = map.zoom*.bind(map) binds the zoom methods so that they will still treat their thises as the map rather than the scope.

Bringing it all together

You can Fiddle around with a working Angular Carto map with custom buttons below.

Why would you do such a thing?

Carto/Leaflet maps come with a built-in zoom control. We had to explicitly turn it off with the option zoomControl: false. What’s so great about writing our own control in an Angular directive instead of using the built-in control? The main reason is that it gives us control over the look-and-feel of the zoom controls. If the built in Leaflet controls clash with the styling we’re using in the rest of the site, we can create buttons with the same functionality but a more appropriate appearance. Still not convinced? That’s okay. You’ve learned a lot about Angular directives, and that will serve you well in the rest of the series.

Other posts in this series

Friday, June 2, 2017

Angular Carto: Map Factory

This is part of a series covering how to create reusable Carto map components. Read the introduction to learn why one would even want to do such a thing.

In this post, we will discuss how to add a Carto map to a website and how to register the map with Angular so that our Angular components can manipulate the map display.

Reference the scripts

Before jumping into your new Carto map, you will first need to make sure that your web page has references to the Carto and Angular javascript libraries. In order for the map to tile correctly, you will also have to reference Carto’s standard stylesheet.

Sizing web page and containers

If you’re looking for an immersive map that takes up the entire web page, you will need to make sure that the html and body elements and the map container are all styled to fill all space available to them. My preferred method is to define a site-wide style implementing those requirements, including a carto-map-container CSS class that can be applied to the element where the Carto map is inserted.

Creating a container for the map

The map is inserted in a placeholder div element. You will need to assign that element an id. This id will later be passed to Carto to indicate where the map will be created. You will also need to assign the container div to the carto-map-container CSS class, which we defined above to expand to fill all space available. Experience has shown me that the map container div must be the first child of the body element for Carto to work correctly in Internet Explorer.

Registering a map factory with Angular

In a vanilla Carto web page, adding a map is as simple as instantiating the class. (Technically, this is a Leaflet class, but the distinction is not relevant to the present post.) The complication of using Angular is that we will be building Angular components that manipulate the map, so we need a reliable way for those components to find the Leaflet/Carto map object. The method Angular uses for this is dependency injection. As we build angular components throughout this series, they will all take a parameter named map as a parameter. The Angular dependency injector will inspect each component’s constructor and provide the map object registered under the name “map” as the argument to the map parameter.

To register the map with Angular, we will register an anonymous function that instantiates and configures the map as factory under the name “map”. (The many options and functions to customize Leaflet/Carto map objects are beyond the scope of this post, so review the Leaflet documentation if you need to learn more.) Once another Angular component requests the map by having a parameter named “map” in its constructor, Angular will invoke the anonymous function to build the map, and store the resulting value to provide for all future requests of the map. In other words, the map creation function is called only once.

You should also note that there is another dependency injection at work in this code. The first argument of the initializer is the element ID of the Carto map container. Rather than providing this ID as a string literal, map factory takes a parameter mapContainerID. The actual element ID is registered with the Angular dependency injector using the value. Putting the dependency injector in control of where the map is created loosens the coupling between the map factory and the HTML.

Bringing it all together

In a normal application where there’s at least one component that requires the map, all we would have to do is register the map factory. However, the example code doesn’t have any Angular components, so we need to request the map from the Angular dependency injector ourselves. You can safely ignore the window.onload handler in the sample code, because having at least one map-dependent Angular component on your page will obviate the need for it.

You can Fiddle around with a working Angular Carto map below.

Why would you do such a thing?

Why are we registering an anonymous function as an Angular factory, then jumping through hoops to get the Angular dependency injector to invoke the registered function? Why not just instantiate and configure the Leaflet map and be done with it? Good question! The example code provided in this post is definitely more complicated than necessary for the task of putting a map on a web page. Dependency injection shines when we have a complicated application with many components that manipulate the map. Over the course of this series, I think you will begin to see what I mean.

Design pattern bingo

  • Dependency injection
  • Angular provider (factory)

Other posts in this series

Revision history

  • 2017-06-06: The example code in this page originally passed the element ID carto-map of the Carto map container as a string literal to the map initialization function That was poor Angular style and has been rectified.

Wednesday, May 31, 2017

Angular Carto: Introduction

If you work with geospatial data and haven't had a chance to work with Carto, you are missing out. Carto is a platform for creating interactive geospatial visualizations on the web. The canned maps that you can build from the web-based designer are surprisingly powerful, especially if you are already familiar with SQL and CSS. If you are a proficient web programmer, you can use their JavaScript API to customize your map in virtually any way you can imagine.

Carto hits a sweet spot of simplicity and flexibility that suits me nicely. I work on a team whose job is to help our clients make sense of data. The efficiency with which we can produce Carto web applications frees up our time to add value in other areas.

As great as Carto is, one problem that is becoming more pressing is that we're making a lot of the same maps on different data sets. Consequently, we're starting to do a lot of copy-and-paste code reuse. Over the next year, we will be refactoring essential map functionality into reusable components to make setting up and customizing a new map even more efficient. Our framework of choice for this project is Angular.

Angular is a JavaScript framework for developing dynamic web pages. It has a variety of features that interest me, including:

Fundamentally, though, what appeals to me about Angular is that you can use it to encapsulate up a bunch of low-level DOM manipulations into higher level routines that better fit how I think about the behavior of a web page.

As we package the our Carto functionality into Angular components, I will be blogging through the techniques that we used at each step. This introduction will also server as the index for the series as it progresses. Thanks for reading.


Saturday, September 19, 2015

Scheme Sucks

You will never get read without an eye-grabbing headline.

My beef with Schema arose while working through a set of programming problems that are published online. I was interested in using those problems to familiarize myself with the idioms of Scheme and Haskell, with which I am less familiar. The exercise in question asks for the sum of all Fibonacci numbers that do not exceed 10000 and are divisible by 2. For the purposes of this post, the Fibonacci sequence is defined by a recurrence relation: \[ \begin{align} F(0) &= 0 \\ F(1) &= 1 \\ F(n) &= F(n - 2) + F(n - 1). \\ \end{align} \] In an imperative language such as C, this problem can be solved with a program similar to the following one. The logic of this program mimics how a person might solve the problem with a pencil, paper, and a pocket calculator: it keeps a running total of the even Fibonacci numbers as they are computed.

This is the kind of code I could write in my sleep, which is why I am interested in exploring the functional languages. In a purely functional language such as Haskell, there is (loosely speaking) no state. Consequently, the standard looping construct is recursion. The code example below makes use of the convenient fact that the even Fibonacci numbers can be described by a recurrence relation of their own:
\[ \begin{align} F(0) &= 0 \\ F(3) &= 2 \\ F(n) &= F(n-6) + 4F(n-3). \\ \end{align} \] It will be helpful to discuss this function line-by-line.
  1. Base case. The sum of Fibonacci numbers divisible by 2 and not exceeding 0 is 0.
  2. Base case. The sum of Fibonacci numbers divisible by 2 and not exceeding 1 is 0.
  3. Recursive case. For limits greater than 1, uses a helper function to accumulate the sum.
  4. Definition of the helper function that accumulates the sum. The arguments to the helper function are the previous even Fibonacci number $F(n-6)$, the current even Fibonacci number $F(n-3)$, and the sum up to this point.
  5. If the next even Fibonacci number exceeds the limit, then the sum of the sequence is the sum up to this point.
  6. If the next even Fibonacci number does not exceed the limit, then the accumulator function is invoked recursively with the current Fibonacci number, the next Fibonacci number, and the new total.
  7. Recurrence relation for the even Fibonacci numbers, as above.
I can write a very similar algorithm in Scheme. Like in Haskell, the function uses a helper function to accumulate the sum of the sequence recursively.

So far, so good. I've worked out how to code a the solution to the problem recursively in two different functional programming languages. Now, let's get to the fun stuff. The solution to this problem can be expressed much more succinctly by defining a sequence of even Fibonacci numbers and using the takeWhile function to get the entries that do not exceed the limit.

The list of even Fibonacci numbers evenFibs is constructed beginning with the first two even Fibonacci numbers: $F(0)=0$ and $F(3)=2$. Once the first two members of the sequence are present, the rest of the sequence is populated using zipWith. In functional programming, the zip function takes two lists and returns the corresponding pairs in the list. For example, zip([1, 2], ['a', 'b']) returns [(1, 'a'), (2, 'b')]. The function zipWith calls a projection function on each corresponding pair and adds the result of the projection function to the list instead. For example, zipWith (+) [1, 4] [3, 2] returns [4, 6], with the projection function being addition ($+\colon \mathbb{R} \times \mathbb{R} \rightarrow \mathbb{R}$). The list evenFibs is constructed by zipping the list with itself so that each element is paired with its previous element. All you need to do is specify the recurrence relation for even Fibonacci numbers as the projection function to zipWith, and you can generate as many items in the sequence as are needed.

Once the sequence of even Fibonacci numbers has been defined, the takeWhile function can be used to pull numbers from the sequence while they do not exceed the limit. The first parameter to takeWhile, <= limit, is a predicate to check that an entry in the sequence does not exceed the limit. The sum function is then used to compute the sum of the entries extracted by takeWhile.

This approach works for many programming languages that draw on the functional tradition, including ones like C# and Python that are more often used in an imperative style.

Now, let's try writing this in Scheme. Unfortunately, in Scheme sequences of undefined size (like the Haskell lists and C# and Python generators) are a different data type than sequences of defined size. The latter are called lists, and are the most common data structure in Scheme. The former are called streams. I expected I could just define a Fibonacci stream and apply the Scheme take-while function to it. MIT Press has a nice article explaining how to define a Fibonacci stream, so I stole some code from there.

Unfortunately, the many functions in Scheme that operate on lists will not work on streams, so you have to use the stream versions instead (stream-take-while, stream-fold, etc.).
karl@adenauer$ racket -f bad-fibo-sum-stream.scm
null-list?: argument out of domain #<stream>
This is quite disappointing. Unlike Haskell, Python, and C#, Scheme needs to define two libraries with identical semantics depending on whether you need to work with a sequence of determinate or indeterminate length. Consider this the first reason that scheme sucks. The second reason is the ubiquitous linked list data structure. Adding an item to the end of a linked list is an $\Theta(n)$ operation. This means that building a list like the Fibonacci sequence from a recurrence relation is a $\Theta(n^2)$ operation. To get around this limitation, you have to construct the sequence backwards, and then reverse it.

Wednesday, February 4, 2015

Rhetorical bestiary: Survey data

Survey data is well known to be an imperfect measure of the truth. The many pitfalls of survey data mean that the numbers must be taken with some healthy skepticism.
Evidence-free assertions that black people are three times more dishonest than white people in answering surveys about drug use do not a healthy skepticism make.

Sunday, December 14, 2014

Drivers Obeying the Law, Part II

I was biking South down Ladd towards the stoplight at Division, when I heard the Suburban behind me pull around to try a pass. I was going at 25 mph, and there was another car that had just turned off of Division and was heading North in the opposing lane. Once I saw that there was oncoming traffic, I knew there was no way the Suburban could make the pass without causing a head-on collision. But he made the pass anyway! The other car had to slam on their brakes; the right taillight of the Suburban passed a foot from my handlebars.

The stoplight at Division was red, so I caught him seconds after he passed me. I tapped on his window and said that he had passed too close. He had given me only a foot, and the minimum passing distance is my fall height (6 ft). He said that he had been "completely in the other lane," and that I should ride further over. Again, I was riding 25 mph--the speed limit--and had I been riding any further over I would have risked getting doored by a person stepping out of a parked car. He then said to me:

If you want to be a stickler for the rules, just remember that I'm driving a 7,000 pound vehicle, and you weigh maybe 180 pounds. That's your choice.

Remember, I did not slow him down in any way. The light was red while he was passing me. It remained red long enough for me to catch him and exchange several sentences at the stoplight. He gained no time whatsoever. He broke at least two laws and put my body and my life in danger--and the bodies and lives in the car coming towards us--for literally no reason at all. When called out on it insisted that it is my responsibility to stay out of his way.
Tonight's incident won't be in any crash statistics, but that doesn't mean that Clinton and Ladd are safe places to bike. This guy's attitude is too common on Portland bikeways. Drivers almost never allow enough travel distance to complete a pass safely, and end up risking head-on collisions, almost sideswiping bike riders, or both.
You would think that riding the speed limit would stop unsafe passes, but it doesn't. I routinely get honked at, passed too closely, or brake checked. I've even had to endure all three in close succession! I've been honked at going 20 in a 20 on Clinton and on NE 37th; I've been honked at doing 30 in a 30 on SW Steele. I've been passed too closely on the big hill on Woodstock Blvd., while biking 10 over the speed limit! I can't even imagine what it is like out there for people who ride at a gentler pace than I do.

Saturday, December 13, 2014

Alan Dershowitz is an America-hating Bleeding-heart Liberal

About 8 years ago, I remarked to my eye doctor that maybe Alan Dershowitz's torture warrant idea wasn't such a bad one if the alternative was no accountability at all. Apparently he thought Dershowitz was an America-hating bleeding heart liberal, though, because he launched into a rant about how the CIA isn't stupid, they know that torture can produce unreliable information, and they always verify torture intelligence against other information gathered. (Other highlights: the ticking bomb scenario proves that using torture to gather intelligence is always justified; we don't know what is going on, so therefore no one should oppose it.)
Now that we have the Senate report, we know that my eye doctor was hopelessly naive. The CIA did indeed take intelligence gathered under torture as gospel, sometimes to disastrous ends—nearly letting bin Laden's driver get away, torturing innocent people whose names were given up under torture, etc.
I guess I shouldn't be surprised. What would an eye doctor from Bend, Oregon know about the CIA's torture program?