vega / vega

A visualization grammar.
https://vega.github.io/vega
BSD 3-Clause "New" or "Revised" License
11.18k stars 1.5k forks source link

Map chart dose not show on the div #503

Closed dnwick closed 8 years ago

dnwick commented 8 years ago

Hi,

My requirement is to show a world map with a color scale so that each country will be shown with different colors according to attribute value. I found this tutorial [1] which is some what I need and trying to understand how it was done. [1] https://www.mediawiki.org/wiki/Extension:Graph/Interactive_Graph_Tutorial_2 First I tried it to replicate it in Vega live editor and I was able to get the map showing but even though I added all the code correctly It does not show color scales as shown in the sample and throws

DOMException: Failed to execute 'addColorStop' on 'CanvasGradient': The value provided ('#NaNNaNNaN') could not be parsed as a color.

Please find below the vega specification I tried. To make this work it need two data sources which are the data set and the topojson data which were correctly uploaded.

{
  "version": 2,
  "width": 500,
  "height": 260,
  "padding": 12,
  "background": "#edf1f7",
  "data": [
    {
      "name": "highlights",
      "url": "data.csv",//url is not specified here
      "format": {"type": "csv"},
      "transform": [
        {
          "type": "formula",
          "field": "v",
          "expr": "parseFloat(datum[''+currentYear])"
        }
      ]
    },
    {
      "name": "countries",
      "url": "topoJson.json",//url is not specified here
      "format": {"type": "topojson","feature": "countries"},
      "transform": [
        {
          "type": "geopath",
          "value": "data",
          "scale": 80,
          "center": [-180,125],
          "translate": [0,0],
          "projection": "equirectangular"
        },
        {
          "type": "lookup",
          "keys": ["id"],
          "on": "highlights",
          "onKey": "id",
          "as": ["zipped"],
          "default": {"v": null, "country":"No data"}
        }
      ]
    }
  ],
  "signals": [
    {
      "name": "isDragging",
      "init": false,
      "streams": [
        {"type": "@handle:mousedown","expr": "true"},
        {"type": "mouseup","expr": "false"}
      ]
    },
    {
      "name": "scaledHandlePosition",
      "streams": [
        {
          "type": "mousemove[isDragging]",
          "expr": "eventX()",
          "scale": {"name": "yearsScale","invert": true}
        }
      ]
    },
    {
      "name": "currentYear",
      "init": 2000,
      "expr": "clamp(parseInt(scaledHandlePosition),1960,2013)"
    },
    {
      "name": "tooltipSignal",
      "init": {"expr": "{x: 0, y: 0, datum: {} }"}, 
      "streams": [
        {  
          "type": "@map:mouseover",    
          "expr": "{x: eventX(), y: eventY(), datum: eventItem().datum.zipped}"
        },
        {  
          "type": "@map:mouseout",
          "expr": "{x: 0, y: 0, datum: {} }"
        }
      ] 
    }
  ],
  "scales": [
    {
      "name": "yearsScale",
      "type": "linear",
      "zero": false,
      "domain": [1960,2013],
      "range": "width"
    },
    {
      "name": "color",
      "type": "linear",
      "domain": {"data": "countries","field": "zipped.v"},
      "domainMin": 1,
      "zero": false,
      "range":  ["#FFEDBC", "#f83600"]
    }
  ],
  "marks": [
    {
      "name": "yearLabel",
      "type": "text",
      "properties": {
        "enter": {
          "x": {"value": 0},
          "y": {"value": 25},
          "fontSize": {"value": 32},
          "fontWeight": {"value": "bold"},
          "fill": {"value": "steelblue"}
        },
        "update": {"text": {"signal": "currentYear"}}
      }
    },
    {
      "name": "scrollLine",
      "type": "rule",
      "properties": {
        "enter": {
          "x": {"value": 0},
          "y": {"value": 40},
          "x2": {"value": 500},
          "stroke": {"value": "#000"},
          "strokeWidth": {"value": 2}
        }
      }
    },
    {
      "name": "handle",
      "type": "path",
      "properties": {
        "enter": {
          "y": {"value": 40},
          "path": {"value": "m-5.5,-10l0,20l11.5,-10l-11.5,-10z"},
          "stroke": {"value": "#880"},
          "strokeWidth": {"value": 2.5}
        },
        "update": {
          "x": {"scale": "yearsScale","signal": "currentYear"},
          "fill": {"value": "#fff"}
        },
        "hover": {"fill": {"value": "#f00"}}
      }
    },
    {
      "name": "map",
      "type": "path",
      "from": {"data": "countries"},
      "properties": {
        "enter": {"path": {"field": "layout_path"}},
        "update": {
          "fill":{
            "rule": [
              {
                "predicate": {
                  "name": "isNotNull",
                  "id": {"field": "zipped.v"}
                },
                "scale": "color",
                "field": "zipped.v"
              },
              {"value": "grey"}
            ]
          }
        },
        "hover": {"fill": {"value": "#989898"}}
      }
    },
    {
      "type": "text",
      "properties": {
        "enter": {
          "x": {"value": 500},
          "y": {"value": 10},
          "align": {"value": "right"},
          "fontSize": {"value": 17},
          "fill": {"value": "black"}
        },
        "update": {
          "text": {"template": "\u007b{tooltipSignal.datum.country}} \u007b{tooltipSignal.datum.v}}"}
        }
      }
    }
  ],
  "predicates": [
    {
      "name": "isNotNull",
      "type": "!=",
      "operands": [{"value": null}, {"arg": "id"}]
    }
  ],
  "legends": [
    {
      "fill": "color",
      "title": "Fertility",
      "offset":-300,
      "properties": {
        "gradient": {
          "stroke": {"value": "transparent"}
        },
        "title": {
          "fontSize": {"value": 14}
       }
      }
    }
  ]
}

worldmapdraw1

Also I like to clarify some points regarding the topojson. I have read the documentation but as it contains data with counties ISO codes , how can I map the real country name to that? Do I have to keep a separate json for that ? Also If I want to draw only the countries in Europe or states in USA how can obtain the corresponding topojson file ?

Regarding the Issue I mentioned as the subject of this issue, Since above specification gives said error I have removed some of the configuration and got the map which now highlights when hovers (But colors not showing). I have add the same configuration to the app I'm building but it does not show the map.

Please let me know if I missing any configuration. Below is my map initializing and draw functions.


//these arguments are not used since this a tryout and specification has the data needed
var map = function(dataTable, config) {

    this.metadata = dataTable[0].metadata;
    config = checkConfig(config, this.metadata);
    this.config = config;

    this.spec = {

        "version": 2,
        "width": 500,
        "height": 260,
        "padding": 12,
        "background": "#edf1f7",
        "data": [
            {
                "name": "highlights",
                "url": "http://localhost:63342/VizGrammar/samples/csvData/data.csv",
                "format": {"type": "csv"},
                "transform": [
                    {
                        "type": "formula",
                        "field": "v",
                        "expr": "parseFloat(datum[''+currentYear])"
                    }
                ]
            },
            {
                "name": "countries",
                "data": // topojson is provided here correctly
                "format": {"type": "topojson","feature": "countries"},
                "transform": [
                    {
                        "type": "geopath",
                        "value": "data",
                        "scale": 80,
                        "center": [-180,125],
                        "translate": [0,0],
                        "projection": "equirectangular"
                    },
                    {
                        "type": "lookup",
                        "keys": ["id"],
                        "on": "highlights",
                        "onKey": "id",
                        "as": ["zipped"],
                        "default": {"v": null, "country":"No data"}
                    }
                ]
            }
        ],
        "signals": [
            {
                "name": "isDragging",
                "init": false,
                "streams": [
                    {"type": "@handle:mousedown","expr": "true"},
                    {"type": "mouseup","expr": "false"}
                ]
            },
            {
                "name": "scaledHandlePosition",
                "streams": [
                    {
                        "type": "mousemove[isDragging]",
                        "expr": "eventX()",
                        "scale": {"name": "yearsScale","invert": true}
                    }
                ]
            },
            {
                "name": "currentYear",
                "init": 2000,
                "expr": "clamp(parseInt(scaledHandlePosition),1960,2013)"
            },
            {
                "name": "tooltipSignal",
                "init": {"expr": "{x: 0, y: 0, datum: {} }"},
                "streams": [
                    {
                        "type": "@map:mouseover",
                        "expr": "{x: eventX(), y: eventY(), datum: eventItem().datum.zipped}"
                    },
                    {
                        "type": "@map:mouseout",
                        "expr": "{x: 0, y: 0, datum: {} }"
                    }
                ]
            }
        ],
        "scales": [
            {
                "name": "yearsScale",
                "type": "linear",
                "zero": false,
                "domain": [1960,2013],
                "range": "width"
            },
            {
                "name": "color",
                "type": "linear",
                "domain": {"data": "countries","field": "zipped.v"},
                "domainMin": 1,
                "zero": false,
                "range":  ["#FFEDBC", "#f83600"]
            }
        ],
        "marks": [
            {
                "name": "yearLabel",
                "type": "text",
                "properties": {
                    "enter": {
                        "x": {"value": 0},
                        "y": {"value": 25},
                        "fontSize": {"value": 32},
                        "fontWeight": {"value": "bold"},
                        "fill": {"value": "steelblue"}
                    },
                    "update": {"text": {"signal": "currentYear"}}
                }
            },
            {
                "name": "scrollLine",
                "type": "rule",
                "properties": {
                    "enter": {
                        "x": {"value": 0},
                        "y": {"value": 40},
                        "x2": {"value": 500},
                        "stroke": {"value": "#000"},
                        "strokeWidth": {"value": 2}
                    }
                }
            },
            {
                "name": "handle",
                "type": "path",
                "properties": {
                    "enter": {
                        "y": {"value": 40},
                        "path": {"value": "m-5.5,-10l0,20l11.5,-10l-11.5,-10z"},
                        "stroke": {"value": "#880"},
                        "strokeWidth": {"value": 2.5}
                    },
                    "update": {
                        "x": {"scale": "yearsScale","signal": "currentYear"},
                        "fill": {"value": "#fff"}
                    },
                    "hover": {"fill": {"value": "#f00"}}
                }
            },
            {
                "name": "map",
                "type": "path",
                "from": {"data": "countries"},
                "properties": {
                    "enter": {"path": {"field": "layout_path"}},
                    "update": {
                        "fill":{
                            "rule": [
                                {
                                    "predicate": {
                                        "name": "isNotNull",
                                        "id": {"field": "zipped.v"}
                                    },
                                    "scale": "color",
                                    "field": "zipped.v"
                                },
                                {"value": "grey"}
                            ]
                        }
                    },
                    "hover": {"fill": {"value": "#989898"}}
                }
            },
            {
                "type": "text",
                "properties": {
                    "enter": {
                        "x": {"value": 500},
                        "y": {"value": 10},
                        "align": {"value": "right"},
                        "fontSize": {"value": 17},
                        "fill": {"value": "black"}
                    },
                    "update": {
                        "text": {"template": "\u007b{tooltipSignal.datum.country}} \u007b{tooltipSignal.datum.v}}"}
                    }
                }
            }
        ],
        "predicates": [
            {
                "name": "isNotNull",
                "type": "!=",
                "operands": [{"value": null}, {"arg": "id"}]
            }
        ]

    };

};

map.prototype.draw = function(div) {
    var viewUpdateFunction = (function(chart) {
        this.view = chart({el:div}).update();
    }).bind(this);

    vg.parse.spec(this.spec, viewUpdateFunction);
};

For above specification I only call draw method which passes the div which the map need to be plotted. But it only shows following with no errors on the console.

mapdraw2

Also I have tried the map example in vega editor but when I add the specification to above function it says Uncaught TypeError: d3.geo[p] is not a function. I really appreciate if you could provide any help on above.

dnwick commented 8 years ago

It seems I cannot give topojson as a inline source other than url. On my app I have given not the url but as values. To test it I have given the same on Vega editor and it did not draw the map. As you can see I mistakenly given it as data above but it did not threw any error.

{
      "name": "countries",
      "values": [{jsonValues}],//not specified since its large data set
      "format": {"type": "topojson","feature": "countries"},
      "transform": [
        {
          "type": "geopath",
          "value": "data",
          "scale": 80,
          "center": [-180,125],
          "translate": [0,0],
          "projection": "equirectangular"
        },
        {
          "type": "lookup",
          "keys": ["id"],
          "on": "highlights",
          "onKey": "id",
          "as": ["zipped"],
          "default": {"v": null, "country":"No data"}
        }
      ]
    }
dnwick commented 8 years ago

I found the issue of not showing the colors in vega editor. It seems the csv data is not loaded properly. But the url is working fine. I'm not sure why it is not picking it up. I have given one data set inline and it worked. But I still having issues of getting this work on my app. And even though I give the topojson inline or using an URL it always says TopoJSON library not loaded. I think the issue here is the data set and topojson is not picking up properly. Also I would like to know how should I create different topojson to draw different areas as mentioned above.