Giter Club home page Giter Club logo

ps-dispatch'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  avatar  avatar  avatar  avatar

Watchers

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

ps-dispatch's Issues

[ISSUE]

I see alerts only in the mdt

/911 not worked with qs-smartphone

it's not worked with qs-smartphone ... everytime i do /911, it says you don't have phone.. cause that phone spawn code is different.. and i tried to change phone codes from ps-dispatch but it getting me error.. please fix this for qs-smartphone

[BUG] /cleardispatchblips only removes blips and not the radius.

like the title says, the /cleardispatchblips only removes blips and not the radius. Sometimes the blip has a radius attached with it, like so

image

after using the command /cleardispatchblips the following happens:

image

only the blips gets removed and the radius attached to the blip remains.

the bug is that the blip gets removed but the attached radius remains on the map.

random story robbery

one guy hit the store and in map all pop oout storerobbery in different palces

exports alerting on cops and not actual robberies

I am noticing for example my qb-storerobbery that when i set the export for dispatch in the robbery script to alert cops, that it will actually put the blips on the police and not the actual robbery.

pd alerting alerts

is there a way to make it so certain jobs such as police doesnt alert the alerts. i cant seem to find it if so

A way to see all of the current calls active/clearing calls overtime?

Some form of dropdown ui on the right hand side of your screen similar to no pixel, where it shows a list of all active dispatch calls, and then maybe have a check, so after 10 minutes of a call being created if no one attaches to it, the dispatch automatically clears the call? This would help keep things clear and allow the police to easily see all current crimes being completed.

Then a way to actually clear calls?

Some of this could already exist and im dumb, but I couldn't see any of this implemented yet.

Off Duty bug

When it's set to Config.OnDutyOnly = true and you're off duty and then go on duty you will hear the notification sounds but not get the alert on the right. The sound for the notification still plays but doesn't show the alert.

Speeding vehicles

want to reduce the amount of speeding vehicles notification. is it possible to setup speeding cameras at specific locations?

Multi Job support for Debug

HI! I attempted to make this work via the config.policejobs but I was unable to get the dispatch to stop alerting for shots fired calls even when debug was false. I attempted to use the fix provided in PS discord for cl_loops adjustment but couldn't get it to work for multiple police jobs.

Other law enforcement orgs not showing up

So my QB police job is configured to have both Police and Sheriff under one job and I am having problems with getting the Guys who are BCSO to show up on the MDT for where has how many are on duty, I went in to the client and server Lua to add in sheriff and anyone under the Sheriff job only shows up as unknown But I have it now where they are able to access the MDT but when they are on the roster they are unknown

[ADDING] doorcount ,callsign and weapon names + maybe something u could add

qb-dispatch -> app.js ( copy in line 98 ):

    if (info['callsign']) {
        DispatchItem += `<div class="call-bottom-info"><span class="fas fa-solid fa-eye"></span>${info['callsign']}</div>`
    }

    if (info['doorCount']) {
        DispatchItem += `<div class="call-bottom-info"><span class="fas fa-door-open"></span>${info['doorCount']}</div>`
    }

    if (info['weapon']) {
        DispatchItem += `<div class="call-bottom-info"><span class="fas fa-bullseye"></span>${info['weapon']}</div>`
    }

qb-mdt -> app.js (copy in line 4068):

        if (value["callsign"]) {
          DispatchItem += `<div class="call-bottom-info"><span class="fas fa-compass"></span>${value.callsign}</div>`;
        }

        if (value["doorCount"]) {
          DispatchItem += `<div class="call-bottom-info"><span class="fas fa-door-open"></span>${value.doorCount}</div>`;
        }

        if (value["weapon"]) {
          DispatchItem += `<div class="call-bottom-info"><span class="fas fa-bullseye"></span>${value.weapon}</div>`;
        }

cl_events.lua:

--doorcount and weapon info edit:
local function VehicleShooting(vehdata)
    local vehicle = QBCore.Functions.GetClosestVehicle()
    local vehdata = vehicleData(vehicle)
    local currentPos = GetEntityCoords(PlayerPedId())
    local locationInfo = getStreetandZone(currentPos)
    local heading = getCardinalDirectionFromHeading()
    local gender = GetPedGender()
    local doorCount = 0
    local weapon = nil
    local PlayerPed = PlayerPedId()
    local CurrentWeapon = GetSelectedPedWeapon(PlayerPed)
    if CurrentWeapon == 584646201 then weapon = "CLASS 2: AP-Pistole" elseif CurrentWeapon == 453432689 then weapon = "CLASS 1: Pistole" elseif CurrentWeapon == 3219281620 then weapon = "CLASS 1: Pistole MK2" elseif CurrentWeapon == 1593441988 then weapon = "CLASS 1: Combat Pistole" elseif CurrentWeapon == -1716589765 then weapon = "CLASS 1: Heavy Pistole" elseif CurrentWeapon == -1076751822 then weapon = "CLASS 1: SNS-Pistole" elseif CurrentWeapon == -771403250 then weapon = "CLASS 2: Desert Eagle" elseif CurrentWeapon == 137902532 then weapon = "CLASS 2: Vintage Pistole" elseif CurrentWeapon == -598887786 then weapon = "CLASS 2: Marksman Pistole" elseif CurrentWeapon == -1045183535 then weapon = "CLASS 2: Revolver" elseif CurrentWeapon == 911657153 then weapon = "Taser" elseif CurrentWeapon == 324215364 then weapon = "CLASS 2: Micro-SMG" elseif CurrentWeapon == -619010992 then weapon = "CLASS 2: Maschinen-Pistole" elseif CurrentWeapon == 736523883 then weapon = "CLASS 2: SMG" elseif CurrentWeapon == 2024373456 then weapon = "CLASS 2: SMG MK2" elseif CurrentWeapon == -270015777 then weapon = "CLASS 2: Assault SMG" elseif CurrentWeapon == 171789620 then weapon = "CLASS 2: Combat PDW" elseif CurrentWeapon == -1660422300 then weapon = "CLASS 4: MG" elseif CurrentWeapon == -1660422300 then weapon = "CLASS 4: Combat MG" elseif CurrentWeapon == 3686625920 then weapon = "CLASS 4: Combat MG MK2" elseif CurrentWeapon == 1627465347 then weapon = "CLASS 4: Gusenberg" elseif CurrentWeapon == -1121678507 then weapon = "CLASS 2: Mini SMG" elseif CurrentWeapon == -1074790547 then weapon = "CLASS 3: Assaultrifle" elseif CurrentWeapon == 961495388 then weapon = "CLASS 3: Assaultrifle MK2" elseif CurrentWeapon == -2084633992 then weapon = "CLASS 3: Carbinerifle" elseif CurrentWeapon == 4208062921 then weapon = "CLASS 3: Carbinerifle MK2" elseif CurrentWeapon == -1357824103 then weapon = "CLASS 3: Advancedrifle" elseif CurrentWeapon == -1063057011 then weapon = "CLASS 3: Specialcarbine" elseif CurrentWeapon == 2132975508 then weapon = "CLASS 3: Bulluprifle" elseif CurrentWeapon == 1649403952 then weapon = "CLASS 3: Compactrifle" elseif CurrentWeapon == 100416529 then weapon = "CLASS 4: Sniperrifle" elseif CurrentWeapon == 205991906 then weapon = "CLASS 4: Heavy Sniper" elseif CurrentWeapon == 177293209 then weapon = "CLASS 4: Heavy Sniper MK2" elseif CurrentWeapon == -952879014 then weapon = "CLASS 4: Marksmanrifle" elseif CurrentWeapon == 487013001 then weapon = "CLASS 2: Pumpshotgun" elseif CurrentWeapon == 2017895192 then weapon = "CLASS 2: Sawnoff Shotgun" elseif CurrentWeapon == -1654528753 then weapon = "CLASS 3: Bullupshotgun" elseif CurrentWeapon == -494615257 then weapon = "CLASS 3: Assaultshotgun" elseif CurrentWeapon == -1466123874 then weapon = "CLASS 3: Musket" elseif CurrentWeapon == 984333226 then weapon = "CLASS 3: Heavyshotgun" elseif CurrentWeapon == -275439685 then weapon = "CLASS 2: Doublebarrel Shotgun" elseif CurrentWeapon == 317205821 then weapon = "CLASS 2: Autoshotgun" elseif CurrentWeapon == -1568386805 then weapon = "CLASS 5: GRENADE LAUNCHER" elseif CurrentWeapon == -1312131151 then weapon = "CLASS 5: RPG" elseif CurrentWeapon == 125959754 then weapon = "CLASS 5: Compactlauncher" else weapon = "UNBEKANNT "end
	if GetEntityBoneIndexByName(vehicle, 'door_pside_f') ~= -1 then doorCount = doorCount + 1 end
	if GetEntityBoneIndexByName(vehicle, 'door_pside_r') ~= -1 then doorCount = doorCount + 1 end
	if GetEntityBoneIndexByName(vehicle, 'door_dside_f') ~= -1 then doorCount = doorCount + 1 end
	if GetEntityBoneIndexByName(vehicle, 'door_dside_r') ~= -1 then doorCount = doorCount + 1 end
	if doorCount == 2 then doorCount = "Zweitürer" elseif doorCount == 3 then doorCount = "Dreitürer" elseif doorCount == 4 then doorCount = "Viertürer" else doorCount = "UNBEKANNT" end
    TriggerServerEvent("dispatch:server:notify",{
        dispatchcodename = "vehicleshots", -- has to match the codes in sv_dispatchcodes.lua so that it generates the right blip
        dispatchCode = "10-60",
        firstStreet = locationInfo,
        model = vehdata.name,
        plate = vehdata.plate,
        gender = gender,
        weapon = weapon,
        doorCount = doorCount,
        priority = 1,
        firstColor = vehdata.colour,
        heading = heading,
        automaticGunfire = false,
        origin = {
            x = currentPos.x,
            y = currentPos.y,
            z = currentPos.z
        },
        dispatchMessage = "SCHÜSSE GEFALLEN",
        job = {"police"}
    })
end exports('VehicleShooting', VehicleShooting)

local function Shooting()
    local currentPos = GetEntityCoords(PlayerPedId())
    local locationInfo = getStreetandZone(currentPos)
    local gender = GetPedGender()
    local PlayerPed = PlayerPedId()
    local weapon = nil
    local PlayerPed = PlayerPedId()
    local CurrentWeapon = GetSelectedPedWeapon(PlayerPed)
    if CurrentWeapon == 584646201 then weapon = "CLASS 2: AP-Pistole" elseif CurrentWeapon == 453432689 then weapon = "CLASS 1: Pistole" elseif CurrentWeapon == 3219281620 then weapon = "CLASS 1: Pistole MK2" elseif CurrentWeapon == 1593441988 then weapon = "CLASS 1: Combat Pistole" elseif CurrentWeapon == -1716589765 then weapon = "CLASS 1: Heavy Pistole" elseif CurrentWeapon == -1076751822 then weapon = "CLASS 1: SNS-Pistole" elseif CurrentWeapon == -771403250 then weapon = "CLASS 2: Desert Eagle" elseif CurrentWeapon == 137902532 then weapon = "CLASS 2: Vintage Pistole" elseif CurrentWeapon == -598887786 then weapon = "CLASS 2: Marksman Pistole" elseif CurrentWeapon == -1045183535 then weapon = "CLASS 2: Revolver" elseif CurrentWeapon == 911657153 then weapon = "Taser" elseif CurrentWeapon == 324215364 then weapon = "CLASS 2: Micro-SMG" elseif CurrentWeapon == -619010992 then weapon = "CLASS 2: Maschinen-Pistole" elseif CurrentWeapon == 736523883 then weapon = "CLASS 2: SMG" elseif CurrentWeapon == 2024373456 then weapon = "CLASS 2: SMG MK2" elseif CurrentWeapon == -270015777 then weapon = "CLASS 2: Assault SMG" elseif CurrentWeapon == 171789620 then weapon = "CLASS 2: Combat PDW" elseif CurrentWeapon == -1660422300 then weapon = "CLASS 4: MG" elseif CurrentWeapon == -1660422300 then weapon = "CLASS 4: Combat MG" elseif CurrentWeapon == 3686625920 then weapon = "CLASS 4: Combat MG MK2" elseif CurrentWeapon == 1627465347 then weapon = "CLASS 4: Gusenberg" elseif CurrentWeapon == -1121678507 then weapon = "CLASS 2: Mini SMG" elseif CurrentWeapon == -1074790547 then weapon = "CLASS 3: Assaultrifle" elseif CurrentWeapon == 961495388 then weapon = "CLASS 3: Assaultrifle MK2" elseif CurrentWeapon == -2084633992 then weapon = "CLASS 3: Carbinerifle" elseif CurrentWeapon == 4208062921 then weapon = "CLASS 3: Carbinerifle MK2" elseif CurrentWeapon == -1357824103 then weapon = "CLASS 3: Advancedrifle" elseif CurrentWeapon == -1063057011 then weapon = "CLASS 3: Specialcarbine" elseif CurrentWeapon == 2132975508 then weapon = "CLASS 3: Bulluprifle" elseif CurrentWeapon == 1649403952 then weapon = "CLASS 3: Compactrifle" elseif CurrentWeapon == 100416529 then weapon = "CLASS 4: Sniperrifle" elseif CurrentWeapon == 205991906 then weapon = "CLASS 4: Heavy Sniper" elseif CurrentWeapon == 177293209 then weapon = "CLASS 4: Heavy Sniper MK2" elseif CurrentWeapon == -952879014 then weapon = "CLASS 4: Marksmanrifle" elseif CurrentWeapon == 487013001 then weapon = "CLASS 2: Pumpshotgun" elseif CurrentWeapon == 2017895192 then weapon = "CLASS 2: Sawnoff Shotgun" elseif CurrentWeapon == -1654528753 then weapon = "CLASS 3: Bullupshotgun" elseif CurrentWeapon == -494615257 then weapon = "CLASS 3: Assaultshotgun" elseif CurrentWeapon == -1466123874 then weapon = "CLASS 3: Musket" elseif CurrentWeapon == 984333226 then weapon = "CLASS 3: Heavyshotgun" elseif CurrentWeapon == -275439685 then weapon = "CLASS 2: Doublebarrel Shotgun" elseif CurrentWeapon == 317205821 then weapon = "CLASS 2: Autoshotgun" elseif CurrentWeapon == -1568386805 then weapon = "CLASS 5: GRENADE LAUNCHER" elseif CurrentWeapon == -1312131151 then weapon = "CLASS 5: RPG" elseif CurrentWeapon == 125959754 then weapon = "CLASS 5: Compactlauncher" else weapon = "UNBEKANNT "end
    TriggerServerEvent("dispatch:server:notify",{
        dispatchcodename = "shooting", -- has to match the codes in sv_dispatchcodes.lua so that it generates the right blip
        dispatchCode = "10-11",
        firstStreet = locationInfo,
        gender = gender,
        weapon = weapon,
        model = nil,
        plate = nil,
        priority = 1,
        firstColor = nil,
        automaticGunfire = false,
        origin = {
            x = currentPos.x,
            y = currentPos.y,
            z = currentPos.z
        },
        dispatchMessage = "SCHÜSSE GEFALLEN",
        job = {"police"}
    })
end exports('Shooting', Shooting)

local function SpeedingVehicle(vehdata)
    local vehicle = QBCore.Functions.GetClosestVehicle()
    local vehdata = vehicleData(vehicle)
    local currentPos = GetEntityCoords(PlayerPedId())
    local locationInfo = getStreetandZone(currentPos)
    local heading = getCardinalDirectionFromHeading()
    local gender = GetPedGender()
    local doorCount = 0
	if GetEntityBoneIndexByName(vehicle, 'door_pside_f') ~= -1 then doorCount = doorCount + 1 end
	if GetEntityBoneIndexByName(vehicle, 'door_pside_r') ~= -1 then doorCount = doorCount + 1 end
	if GetEntityBoneIndexByName(vehicle, 'door_dside_f') ~= -1 then doorCount = doorCount + 1 end
	if GetEntityBoneIndexByName(vehicle, 'door_dside_r') ~= -1 then doorCount = doorCount + 1 end
	if doorCount == 2 then doorCount = "Zweitürer" elseif doorCount == 3 then doorCount = "Dreitürer" elseif doorCount == 4 then doorCount = "Viertürer" else doorCount = "UNBEKANNT" end
    TriggerServerEvent("dispatch:server:notify",{
        dispatchcodename = "speeding", -- has to match the codes in sv_dispatchcodes.lua so that it generates the right blip
        dispatchCode = "10-11",
        firstStreet = locationInfo,
        model = vehdata.name,
        plate = vehdata.plate,
        gender = gender,
        doorCount = doorCount,
        priority = 2,
        firstColor = vehdata.colour,
        heading = heading,
        automaticGunfire = false,
        origin = {
            x = currentPos.x,
            y = currentPos.y,
            z = currentPos.z
        },
        dispatchMessage = "Speeding Vehicle",
        job = {"police"}
    })
end exports('SpeedingVehicle', SpeedingVehicle)

--callsign edit:
local function OfficerDown()
    local plyData = QBCore.Functions.GetPlayerData()
    local currentPos = GetEntityCoords(PlayerPedId())
    local locationInfo = getStreetandZone(currentPos)
    local callsign = QBCore.Functions.GetPlayerData().metadata["callsign"]
    TriggerServerEvent("dispatch:server:notify",{
        dispatchcodename = "officerdown", -- has to match the codes in sv_dispatchcodes.lua so that it generates the right blip
        dispatchCode = "10-99",
        firstStreet = locationInfo,
        name = "COP - " ..plyData.charinfo.firstname:sub(1,1):upper()..plyData.charinfo.firstname:sub(2).. " ".. plyData.charinfo.lastname:sub(1,1):upper()..plyData.charinfo.lastname:sub(2),
        model = nil,
        plate = nil,
        callsign = callsign,
        priority = 1, -- priority
        firstColor = nil,
        automaticGunfire = false,
        origin = {
            x = currentPos.x,
            y = currentPos.y,
            z = currentPos.z
        },
        dispatchMessage = "Officer down", -- message
        job = {"police", "ambulance"} -- jobs that will get the alerts
    })
end exports('OfficerDown', OfficerDown)

local function EmsDown()
    local plyData = QBCore.Functions.GetPlayerData()
    local currentPos = GetEntityCoords(PlayerPedId())
    local locationInfo = getStreetandZone(currentPos)
    local callsign = QBCore.Functions.GetPlayerData().metadata["callsign"]
    TriggerServerEvent("dispatch:server:notify",{
        dispatchcodename = "emsdown", -- has to match the codes in sv_dispatchcodes.lua so that it generates the right blip
        dispatchCode = "10-99",
        firstStreet = locationInfo,
        name = "EMS - " ..plyData.charinfo.firstname:sub(1,1):upper()..plyData.charinfo.firstname:sub(2).. " ".. plyData.charinfo.lastname:sub(1,1):upper()..plyData.charinfo.lastname:sub(2),
        model = nil,
        plate = nil,
        callsign = callsign,
        priority = 1, -- priority
        firstColor = nil,
        automaticGunfire = false,
        origin = {
            x = currentPos.x,
            y = currentPos.y,
            z = currentPos.z
        },
        dispatchMessage = "EMS Down", -- message
        job = {"police", "ambulance"} -- jobs that will get the alerts
    })
end exports('EmsDown', EmsDown) 

Would love to see RED dispatches for police too, for officer down or officer down urgent for example:

BrabMSV

like this but for the cops

Dear LeSiiN <3

Autotheft alerts are not implemented

In the config there is an option for Config.Enable.Autotheft, however there is actually no implementation for Autotheft alerts (at least I was unable to find them)

vehdata.name problem

Hello,

I am using qb-dispatch with qb-vehiclekeys and qb-lock. I am getting an error on client/cl_extraalerts.lua line 182. I am assuming it is not able to draw the vehicle info properly to export. Can you tell me what other options I can try here? Gunshot, gunshots from vehicle, drug sale, officer down, speeding all work properly...I just cant get this properly. Thank you for your help
error
!

Police Alerts

I have Config.Debug set to false but it still shows alert for police shooting.

Also active units only shows myself even if others are on duty

No Phone /Error/

When I try to use /911 it says that I don't have a phone, but I have one. Does anybody know a fix to this?

Config.OnDutyOnly breaks top-right UI alerts.

When Config.OnDutyOnly = true, the cops(even if they on or off duty) don't get the top-right alerts UI (only sound), but they get it in the MDT.
I've set Config.OnDutyOnly = false and it works. So I guess it's a lingering issue where if you enable the onduty it breaks the UI alert top-right.

For anyone that want the onduty feature to work:

Replace this line in cl_main.lua

if IsValidJob(sNotificationData['job']) and CheckOnDuty() then

with:

if IsValidJob(sNotificationData['job']) and QBCore.Functions.GetPlayerData().job.onduty then

and you'll be good to go untill they fix it.

I tried:

if IsValidJob(sNotificationData['job']) and PlayerJob.onduty then

but it doesn't work.

ambulance

ambulance job does not get notifications is dashboard and in the screen when someone is injured or dead

blipLength not functioning properly

Blips remain on the map/minimap for far too long. All blipLengths are set to "2" in sv_dispatchcodes.lua which should mean that blips start to fade away after 2 seconds, but blips stay on the map/minimap for up to 3 minutes.

Add 911 alerts?

I don't know if this is possible or not, but could be a great enhancement to this script

EmergencyButton

[ script:qb-policejob] SCRIPT ERROR: citizen:/scripting/lua/scheduler.lua:867: No such export OfficerDown in resource qb-dispatch

#bug

OnDutyOnly doesn't work.

Error when two or more players attach/detach to calls.

When a call is shown in your "Calls" on the MDT and you right click to attach/detach, if you do it at the same time as someone else in the server in bugs out and your unable to remove/attach yourself from that call.

screenshot of the error:
chrome_cmegtEySZc

MDT Will not open

I have all the dependencies but when I type/mdt it doesn't open and this script error ( citizen:/scripting/lua/scheduler.lua:867: No such export GetDispatchCalls in resource ps-dispatch) shows.

Any idea how I fix this?

/911 /911a do not post to mdt

hey, I may be stupid but 911 and 911a don't seem to post to mdt.
triggered calls like ai vehicle thefts or firearm discharging does just not 911 or 911a

I have ensured ps-dispatch is running before ps-mdt.

Jail directly from MDT

Send player to jail, charge the player after submitting reporting and reading sentence.

911 Command not working

When doing /911 (message) it says you cant call with out a phone and i have one in my inventory

Alert

Im not getting the alert of drugs sales or robberys

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.