Giter Site home page Giter Site logo

lua-resty-maxminddb's Introduction

Name

lua-resty-maxminddb - A Lua library for reading MaxMind's Geolocation database format (aka mmdb or geoip2).

Prerequisites

Note

Bug fixed

Apology for infringement

Installation

# opm (manual install libmaxminddb and download GeoLite2-City.mmdb)
# openresty/openresty:alpine and apache/apisix:2.13.0-alpine docker image need to install perl libmaxminddb
# e.g. apk --no-cache add perl libmaxminddb && ln -s /usr/lib/libmaxminddb.so.0  /usr/lib/libmaxminddb.so
opm get anjia0532/lua-resty-maxminddb

# luarocks (manual download GeoLite2-City.mmdb)
# openresty/openresty:alpine-fat docker image
luarocks install lua-resty-maxminddb

# openresty/openresty:alpine docker image need to install luarocks (ref https://github.com/openresty/docker-openresty/blob/master/alpine/Dockerfile.fat)

# special apache/apisix:2.xx.0-alpine luarocks install lua-resty-maxminddb UNZIP=/usr/bin/unzip
# e.g. apk --no-cache add perl alpine-sdk && luarocks install lua-resty-maxminddb UNZIP=/usr/bin/unzip

Synopsis

server {
    listen 80;
    server_name localhost;
    location / {
        content_by_lua_block{
            local cjson = require 'cjson'
            local geo = require 'resty.maxminddb'
            if not geo.initted() then
                geo.init("/path/to/GeoLite2-City.mmdb")
            end
            local res,err = geo.lookup(ngx.var.arg_ip or ngx.var.remote_addr) --support ipv6 e.g. 2001:4860:0:1001::3004:ef68

            if not res then
                ngx.log(ngx.ERR,'failed to lookup by ip ,reason:',err)
            end
            ngx.say("full :",cjson.encode(res))
            if ngx.var.arg_node then
               ngx.say("node name:",ngx.var.arg_node," ,value:", cjson.encode(res[ngx.var.arg_node] or {}))
            end
        }
    }
}
  #ipv4
  $ curl localhost/?ip=114.114.114.114&node=city
  
  #ipv6
  #$ curl localhost/?ip=2001:4860:0:1001::3004:ef68&node=country
  
  full :{"city":{"geoname_id":1799962,"names":{"en":"Nanjing","ru":"Нанкин","fr":"Nankin","pt-BR":"Nanquim","zh-CN":"南京","es":"Nankín","de":"Nanjing","ja":"南京市"}},"subdivisions":[{"geoname_id":1806260,"names":{"en":"Jiangsu","fr":"Province de Jiangsu","zh-CN":"江苏省"},"iso_code":"32"}],"country":{"geoname_id":1814991,"names":{"en":"China","ru":"Китай","fr":"Chine","pt-BR":"China","zh-CN":"**","es":"China","de":"China","ja":"**"},"iso_code":"CN"},"registered_country":{"geoname_id":1814991,"names":{"en":"China","ru":"Китай","fr":"Chine","pt-BR":"China","zh-CN":"**","es":"China","de":"China","ja":"**"},"iso_code":"CN"},"location":{"time_zone":"Asia\/Shanghai","longitude":118.7778,"accuracy_radius":50,"latitude":32.0617},"continent":{"geoname_id":6255147,"names":{"en":"Asia","ru":"Азия","fr":"Asie","pt-BR":"Ásia","zh-CN":"亚洲","es":"Asia","de":"Asien","ja":"アジア"},"code":"AS"}}
  node name:city ,value:{"geoname_id":1799962,"names":{"en":"Nanjing","ru":"Нанкин","fr":"Nankin","pt-BR":"Nanquim","zh-CN":"南京","es":"Nankín","de":"Nanjing","ja":"南京市"}}

prettify

full: {
    "city": {
        "geoname_id": 1799962,
        "names": {
            "en": "Nanjing",
            "ru": "Нанкин",
            "fr": "Nankin",
            "pt-BR": "Nanquim",
            "zh-CN": "南京",
            "es": "Nankín",
            "de": "Nanjing",
            "ja": "南京市"
        }
    },
    "subdivisions": [{
            "geoname_id": 1806260,
            "names": {
                "en": "Jiangsu",
                "fr": "Province de Jiangsu",
                "zh-CN": "江苏省"
            },
            "iso_code": "32"
        }
    ],
    "country": {
        "geoname_id": 1814991,
        "names": {
            "en": "China",
            "ru": "Китай",
            "fr": "Chine",
            "pt-BR": "China",
            "zh-CN": "**",
            "es": "China",
            "de": "China",
            "ja": "**"
        },
        "iso_code": "CN"
    },
    "registered_country": {
        "geoname_id": 1814991,
        "names": {
            "en": "China",
            "ru": "Китай",
            "fr": "Chine",
            "pt-BR": "China",
            "zh-CN": "**",
            "es": "China",
            "de": "China",
            "ja": "**"
        },
        "iso_code": "CN"
    },
    "location": {
        "time_zone": "Asia\/Shanghai",
        "longitude": 118.7778,
        "accuracy_radius": 50,
        "latitude": 32.0617
    },
    "continent": {
        "geoname_id": 6255147,
        "names": {
            "en": "Asia",
            "ru": "Азия",
            "fr": "Asie",
            "pt-BR": "Ásia",
            "zh-CN": "亚洲",
            "es": "Asia",
            "de": "Asien",
            "ja": "アジア"
        },
        "code": "AS"
    }
}
node name: city, value: {
    "geoname_id": 1799962,
    "names": {
        "en": "Nanjing",
        "ru": "Нанкин",
        "fr": "Nankin",
        "pt-BR": "Nanquim",
        "zh-CN": "南京",
        "es": "Nankín",
        "de": "Nanjing",
        "ja": "南京市"
    }
}

References

Bug Reports

Please report bugs by filing an issue with our GitHub issue tracker at https://github.com/anjia0532/lua-resty-maxminddb/issues

If the bug is casued by libmaxminddb tracker at https://github.com/maxmind/libmaxminddb/issues

Copyright and License

This module is licensed under the apache LICENSE-2.0 license.

Copyright 2017-now anjia ([email protected])

Licensed under the Apache License, Version 2.0 (the "License");

you may not use this file except in compliance with the License.

You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software

distributed under the License is distributed on an "AS IS" BASIS,

WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

See the License for the specific language governing permissions and

limitations under the License.

lua-resty-maxminddb's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

lua-resty-maxminddb's Issues

查询时内存不断增长--内存泄露

Hi,不知道是不是调用的方式不对,查询的时候内存会一直不断增长。

下面是我写的脚本,可以重现。
环境是:CentOS 7.5.1804, openresty/1.13.6.2

-- test.lua
local geoip = require("resty.maxminddb").new("/usr/local/share/GeoIP/GeoLite2-Country.mmdb")
for i = 1, 200000 do
  local ip = string.format("%s.%s.%s.%s", math.random(1, 255), math.random(1, 255), math.random(1, 255), math.random(1, 255))
  local res, err = geoip:lookup(ip)
  if res and res.country and res.country.iso_code then
    print(i, res.country.iso_code)
  end
end

$ resty test.lua

libmaxminddb 要怎么安装呀

环境:Docker (openresty/openresty:alpine-fat)

FROM openresty/openresty:alpine-fat

RUN opm get anjia0532/lua-resty-maxminddb
RUN apk add --no-cache libmaxminddb

但是初始化的时候报错了

 [error] 63#63: *2046 lua entry thread aborted: runtime error: /usr/local/openresty/site/lualib/resty/maxminddb.lua:165: Error loading shared library libmaxminddb.so: No such file or directory

segfault

local cjson=require 'cjson'
local geo=require 'resty.maxminddb'

local arg_ip=ngx.var.arg_ip
local arg_node=ngx.var.arg_node

ngx.say("IP:",arg_ip,", Node:",arg_node,"<br>")

if not geo.initted() then
        geo.init("/usr/local/openresty/nginx/conf/lua/GeoLite2-City.mmdb")
end



local res,err=geo.lookup(arg_ip or ngx.var.remote_addr)


if not res then
        ngx.say("Please check the ip address you provided: <div style='color:red'>",arg_ip,"</div>")
        ngx.log(ngx.ERR,' failed to lookup by ip , reason :',err)
else
        ngx.say("Result:",cjson.encode(res))

        if arg_node then
                ngx.say("<br>Node name:",arg_node, "<br>Value:",cjson.encode(res[arg_node] or {}))

        end



--      ngx.say(cjson.encode(res['country']))
--      ngx.say(cjson.encode(res['city']['names']['zh-CN']))

end

上面这段代码在 curl -k -L "http://192.168.137.70/lua?ip=111.47.224.182&node=subdivisions" 出现异常了

2018/12/07 15:51:23 [alert] 6466#0: worker process 13218 exited on signal 11
dmesg显示:
[24198.721511] openresty[12676]: segfault at 5b ip 00007f3b320e7e45 sp 00007ffc488d6718 error 4 in libmaxminddb.so.0.0.7[7f3b320e5000+5000]
[24356.262054] openresty[13125]: segfault at 5b ip 00007f3b320e7e45 sp 00007ffc488d6718 error 4 in libmaxminddb.so.0.0.7[7f3b320e5000+5000]
[24417.535463] openresty[13486]: segfault at 5b ip 00007f3b320e7e45 sp 00007ffc488d6718 error 4 in libmaxminddb.so.0.0.7[7f3b320e5000+5000]
[24605.925152] openresty[13030]: segfault at 5b ip 00007f3b320e7e45 sp 00007ffc488d6718 error 4 in libmaxminddb.so.0.0.7[7f3b320e5000+5000]
[24742.409458] traps: openresty[13355] trap stack segment ip:7f3b320e88e0 sp:7ffc488d6700 error:0 in libmaxminddb.so.0.0.7[7f3b320e5000+5000]
[24768.654847] openresty[13453]: segfault at 5b ip 00007f3b320e7e45 sp 00007ffc488d6718 error 4 in libmaxminddb.so.0.0.7[7f3b320e5000+5000]
[24875.143743] traps: openresty[13602] trap stack segment ip:7f3b320e88e0 sp:7ffc488d6700 error:0 in libmaxminddb.so.0.0.7[7f3b320e5000+5000]
[24978.362300] openresty[13628]: segfault at 5b ip 00007f3b320e7e45 sp 00007ffc488d6718 error 4 in libmaxminddb.so.0.0.7[7f3b320e5000+5000]
[24985.536349] openresty[13495]: segfault at 5b ip 00007f3b320e7e45 sp 00007ffc488d6718 error 4 in libmaxminddb.so.0.0.7[7f3b320e5000+5000]
[24986.341160] openresty[13541]: segfault at 5b ip 00007f3b320e7e45 sp 00007ffc488d6718 error 4 in libmaxminddb.so.0.0.7[7f3b320e5000+5000]
[24987.163714] openresty[13677]: segfault at 5b ip 00007f3b320e7e45 sp 00007ffc488d6718 error 4 in libmaxminddb.so.0.0.7[7f3b320e5000+5000]
[25053.506562] openresty[13683]: segfault at 7b ip 00007f3b320e7e45 sp 00007ffc488d6718 error 4 in libmaxminddb.so.0.0.7[7f3b320e5000+5000]
[27040.935473] openresty[13218]: segfault at 5b ip 00007f3b320e7e45 sp 00007ffc488d6718 error 4 in libmaxminddb.so.0.0.7[7f3b320e5000+5000]

Originally posted by @zhannk in #5 (comment)

Error at lookup IP

172.17.0.1 - - [21/May/2018:11:55:04 +0000] "GET / HTTP/1.1" 200 0 "-" "curl/7.54.0"
2018/05/21 11:55:04 [error] 6#6: *5 lua entry thread aborted: runtime error: /usr/local/openresty/site/lualib/resty/maxminddb.lua:323: bad argument #1 to 'concat' (table expected, got nil)
stack traceback:
coroutine 0:
[C]: in function 'concat'
/usr/local/openresty/site/lualib/resty/maxminddb.lua:323: in function 'lookup'
/src/geo.lua:6: in function </src/geo.lua:1>, client: 172.17.0.1, server: , request: "GET / HTTP/1.1", host: "localhost:8081"

Init two instances of maxminddb

I try to copy maxminddb.lua as discussed in (#11) for creating additional instance for other maxmind db. But got error:

23030#23030: *18 lua entry thread aborted: runtime error: /usr/local/openresty/lualib/maxminddb2.lua:120: attempt to redefine 'MMDB_entry_s' at line 4

My code:

local geoip = require "maxminddb"
local orgip = require "maxminddb2"
if not geoip.initted() then
    geoip.init("/usr/share/GeoIP/GeoIP2-Country.mmdb")
end

if not orgip.initted() then
    orgip.init("/usr/share/GeoIP/GeoIP2-ISP.mmdb")
end

maxminddb.lua 丢失城市数据

场景:使用lua-resty-maxminddb 开发两个接口:一个接口返回国家数据,一个接口返回城市数据。

在nginx reload之后,先访问国家数据接口,再访问城市数据接口,底层返回的数据,缺失city数据。正常访问ip是有城市数据的。

{"continent":{"code":"SA","geoname_id":6255150,"names":{"ja":"南アメリカ","zh-CN":"南美洲","es":"Sudamérica","pt-BR":"América do Sul","de":"Südamerika","fr":"Amérique du Sud","ru":"Южная Америка","en":"South America"}},"country":{"names":{"ja":"ブラジル連邦共和国","zh-CN":"巴西","es":"Brasil","pt-BR":"Brasil","de":"Brasilien","fr":"Brésil","ru":"Бразилия","en":"Brazil"},"geoname_id":3469034,"iso_code":"BR"},"registered_country":{"names":{"ja":"ブラジル連邦共和国","zh-CN":"巴西","es":"Brasil","pt-BR":"Brasil","de":"Brasilien","fr":"Brésil","ru":"Бразилия","en":"Brazil"},"geoname_id":3469034,"iso_code":"BR"}}

Feature - ASN Support

It'd be awesome if this library supported looking up ASN numbers from the GeoLite2 ASN DB!

Init two DBs under different names

Not sure if I'm trying to do something wrong here, but if I try and init two separate DBs under two name spaces, only the first DB gets used:

location = /test {
  default_type text/plain;
  content_by_lua_block {
    local cjson = require 'cjson'
    local geo   = require 'resty.maxminddb'
    local asn   = require 'resty.maxminddb'
    if not geo.initted() then
        geo.init("/usr/share/GeoIP/GeoLite2-City.mmdb")
    end
    if not asn.initted() then
        asn.init("/usr/share/GeoIP/GeoLite2-ASN.mmdb")
    end
    local res,err = geo.lookup("202.168.51.151")
    local asn_res,err = asn.lookup("202.168.51.151")

    if not res then
        ngx.log(ngx.ERR,'failed to lookup by ip ,reason:',err)
    end

    resp = {}

    ngx.say(cjson.encode(asn_res))
  }
}

In the above example even though I'm using the ASN DB (which works fine!) results are returned from the City DB. I'm assuming this is something to do with my understanding of how things are scoped? If you switch the order in which they're initted then results are returned from the ASN DB fine.

Thanks for the great module though!

Declaration specifier expected near 'ssize_t'

Hi,
When I include file "local geo = require 'resty.maxminddb'" I get an error:
lua entry thread aborted: runtime error: .../resty_modules/lualib/resty/maxminddb.lua:115: declaration specifier expected near 'ssize_t' at line 73

It seems that should be typedef ssize_t in case if it is not declared.
When I declare ssize_t as long or size_t, the error disappears.

My lua version: 5.1

Suggestion to make this usable by other context

Hi, First off, thanks so much by this project. I find it useful and the code is very solid.

I would like to use this in other contexts, though (other than nginx). i.e. other platforms programmable through a luajit integration. I find that i need to make the following changes to make it work.

https://gist.github.com/shukitchan/dc63b60e5d9c519b540390531d06ece3

Would you consider to reorganize your code a little bit so that it can be used easily in other luajit environments. Thanks.

License issue should be noticed when you copy code from lilien1010/lua-resty-maxminddb

when I checked your source code, I see a better code style with more fretures, I really liked it.
but also I am very familiar with your project while some source code is I think copied from my project https://github.com/lilien1010/lua-resty-maxminddb which is already licensed with Apache LISENCE.

Eg:
there is a comment like https://www.maxmind.com/en/geoip2-databases you should download the mmdb file from maxmind is exactly from my project lua-resty-maxminddb

According to the former opensource LISENCE is already Apache lisenced, BSD lisence is not acceptable.

loop or previous error loading module 无法使用

使用命令安装
opm get anjia0532/lua-resty-maxminddb
安装成功
配置如下:
image

一测试就报错了

[error] 119384#0: *17 lua entry thread aborted: runtime error: content_by_lua(nginx.conf:72):3: loop or previous error loading module 'resty.maxminddb'
openresty 1.13.6.2
这个问题,一直都没找到原因,是因为版本问题吗?

Error at determine a boolean value

I create a mmdb file by using officail perl lib.

The boolean type value read by officail python lib is correct. But use this lib is all false.

I think the problem is line 289.

 elseif data_type == MMDB_DATA_TYPE_BOOLEAN then
      val = entry_data_item.boolean == 1

entry_data_item.boolean is true or false in lua. No need to use equal number 1.

是否需要添加前置lru缓存

-- 我有两个疑问,可以帮我解答一下吗
1、是否需要添加内存的lru缓存来做加速呢?
2、如果是值需要获取一下ip的国家和地域,只缓存国家个地域,是否应该加呢?

Memory leak in version 1.3.2

First of all thanks for your module!

I use the following command within my Dockerfile to install it:

opm get anjia0532/lua-resty-maxminddb

However, in the past days, version has bumped from 1.3.0 to 1.3.2, introducing an issue with memory usage, which constantly raises up to several gigabytes.

When downgrading to 1.3.0, I do not have the issue anymore.

Might this be caused by ea529cd ?

My LUA script loaded within a content_by_lua_file directive looks like the following:

local geo = require 'resty.maxminddb'

if not geo.initted() then
    geo.init("/path/to/GeoLite2-Country.mmdb")
end

local ip = ngx.var.arg_ip or headers["x-real-ip"] or headers["x-forwarded-for"] or ngx.var.remote_addr
local res, err = geo.lookup(ip)

Issue when multiple subdivisions are returned

Hi,

Script will break when multiple subdivisions are returned from maxmind-db and this error will be logged: Expected comma or array end but found T_OBJ_BEGIN at character 980

Tested with this ip: 194.187.249.181 (France)

Script will try to return invalid constructed json that could not be parsed later on:

"subdivisions": [{
"geoname_id": 11071623,
"iso_code": "OCC",
"names": {
"en": "Occitanie",
"fr": "Occitanie"
}
} {
"geoname_id": 3036965,
"iso_code": "09",
"names": {
"de": "Ariège",
"en": "Ariège",
"es": "Arieja",
"fr": "Ariège"
}
}]

Comma is missing between these two objects.

I've personally solved it modifying the _dump_entry_data_list function where array is handled adding additional condition:

if size>0 then
table.insert(resultTab, ",");
end

Here is a print screen:
https://www.dropbox.com/s/u1jwuj03nkmzcbm/Screenshot%202018-07-11%2013.22.44.png?dl=0

Hope this helps,
Thanks

MMDB_free_entry_data_list (entry_data_list=0x23) at maxminddb.c:1860

Hi!
while trying to run some tests along with openresty i am getting segfault:

GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-110.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later http://gnu.org/licenses/gpl.html
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
http://www.gnu.org/software/gdb/bugs/...
Reading symbols from /usr/local/openresty/nginx/sbin/nginx...Reading symbols from /usr/local/openresty/nginx/sbin/nginx...(no debugging symbols found)...done.
(no debugging symbols found)...done.
[New LWP 15418]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `nginx -p /usr/local/openresty/nginx/conf/t/servroot/ -c /usr/local/openresty/ng'.
Program terminated with signal 11, Segmentation fault.
#0 MMDB_free_entry_data_list (entry_data_list=0x23) at maxminddb.c:1860
1860 data_pool_destroy(entry_data_list->pool);
Missing separate debuginfos, use: debuginfo-install openresty-1.13.6.2-1.el7.x86_64
#0 MMDB_free_entry_data_list (entry_data_list=0x23) at maxminddb.c:1860
#1 0x00007f59530a5c14 in lj_vm_ffi_call ()
from /usr/local/openresty/luajit/lib/libluajit-5.1.so.2
#2 0x00007f59530e54a4 in lj_ccall_func ()
from /usr/local/openresty/luajit/lib/libluajit-5.1.so.2
#3 0x00007f59530f96a6 in lj_cf_ffi_meta___call ()
from /usr/local/openresty/luajit/lib/libluajit-5.1.so.2
#4 0x00007f59530a3b23 in lj_BC_FUNCC ()
from /usr/local/openresty/luajit/lib/libluajit-5.1.so.2
#5 0x00000000004e9a89 in ngx_http_lua_run_thread ()
#6 0x00000000004ecd36 in ngx_http_lua_content_by_chunk ()
#7 0x00000000004ec88c in ngx_http_lua_content_handler ()
#8 0x0000000000457aee in ngx_http_core_content_phase ()
#9 0x0000000000452595 in ngx_http_core_run_phases ()
#10 0x000000000045ce73 in ngx_http_process_request ()
#11 0x000000000045d6eb in ngx_http_process_request_line ()
#12 0x0000000000447147 in ngx_epoll_process_events ()
#13 0x000000000043e76b in ngx_process_events_and_timers ()
#14 0x00000000004468a0 in ngx_single_process_cycle ()
#15 0x000000000041f76d in main ()

code that producing this error is:
config of test suite is:
use Test::Nginx::Socket 'no_plan';

our $http_config = <<'EOC';
lua_package_path "/usr/local/openresty/nginx/conf/resty/?.luac;/usr/local/openresty/nginx/conf/resty/?.lua;/usr/local/openresty/nginx/conf/resty/lib/?.luac;/usr/local/openresty/nginx/conf/resty/lib/?.lua;;";
EOC

run_tests();

DATA

=== TEST 1: Geoip UA, Kiev
--- http_config eval: $::http_config
--- config
location = /geoip {
include /usr/local/openresty/nginx/conf/resty/conf/init.conf;
content_by_lua_block {
local remote_addr = '95.134.195.14'

    local geo = require 'lb.geoip'
    local res = geo.lookup(remote_addr)
    if ((res or {}).country or {}).iso_code then
        ngx.say(res['country']['iso_code'])
        ngx.exit(ngx.HTTP_OK)
    end
    ngx.say('FAIL')
}

}

can you help to understand why?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.