NYC Permit Pulse tracks 18+ distinct permit types from the DOB NOW system. Each type has a unique code, color, and emoji for visual identification on the map.
All Permit Types
The app normalizes verbose work_type strings from the NYC Open Data API into short 2-4 letter codes:
Code Label Color Emoji Work Type NB New Building β #00ff88π New Building DM Demolition β #ff2222π₯ Full Demolition GC General Construction β #ff8800π¨ General Construction PL Plumbing β #4466ffπ΅ Plumbing ME Mechanical β #00ccffβοΈ Mechanical SOL Solar β #ffe600βοΈ Solar Photovoltaic SHD Sidewalk Shed β #cc44ffπ Sidewalk Shed SCF Scaffold β #ff44aaπͺ Scaffold FNC Const. Fence β #44ffddπ§ Construction Fence SG Sign β #ffffffπ Sign FND Foundation β #a0522dπͺ¨ Foundation STR Structural β #ff6600π© Structural BLR Boiler β #ff0066π₯ Boiler SPR Sprinklers β #00aaffπ§ Sprinkler EW Earth Work β #88ff00π Earth Work ANT Antenna β #dd00ffπ‘ Antenna CC Curb Cut β #ffaa00π€ Curb Cut STP Standpipe β #0055ffπΏ Standpipe OTH Other β #888888π (any unrecognized type)
Color Palette
Colors are chosen for maximum visibility on the dark isometric map background:
export const WORK_TYPE_COLORS : Record < string , string > = {
NB: '#00ff88' , // bright green β new building
DM: '#ff2222' , // red β demolition
GC: '#ff8800' , // orange β general construction
PL: '#4466ff' , // blue β plumbing
ME: '#00ccff' , // cyan β mechanical
SOL: '#ffe600' , // yellow β solar
SHD: '#cc44ff' , // purple β sidewalk shed
SCF: '#ff44aa' , // pink β scaffold
FNC: '#44ffdd' , // teal β construction fence
SG: '#ffffff' , // white β sign
FND: '#a0522d' , // brown β foundation
STR: '#ff6600' , // deep orange β structural (distinct from GC)
BLR: '#ff0066' , // hot pink-red β boiler
SPR: '#00aaff' , // sky blue β sprinkler
EW: '#88ff00' , // yellow-green β earth work
ANT: '#dd00ff' , // violet β antenna
CC: '#ffaa00' , // amber β curb cut
STP: '#0055ff' , // deep blue β standpipe
OTH: '#888888' , // grey β other
};
Color Selection Criteria
High contrast - All colors pop against the dark map background
Distinct hues - Even color-blind users can distinguish major types
Semantic meaning - Red for demolition, green for new construction, blue for water-related (plumbing/sprinklers)
Emoji Mapping
Emojis provide quick visual recognition in the sidebar and permit drawer:
export const WORK_TYPE_EMOJIS : Record < string , string > = {
NB: 'π' , DM: 'π₯' , GC: 'π¨' , PL: 'π΅' ,
ME: 'βοΈ' , SOL: 'βοΈ' , SHD: 'π' , SCF: 'πͺ' ,
FNC: 'π§' , SG: 'π' , FND: 'πͺ¨' , STR: 'π©' ,
BLR: 'π₯' , SPR: 'π§' , EW: 'π' , ANT: 'π‘' ,
CC: 'π€' , STP: 'πΏ' , OTH: 'π' ,
};
Work Type Normalization
The NYC Open Data work_type field contains verbose strings that need to be normalized:
export function workTypeToCode ( workType : string ) : string {
const wt = workType . toLowerCase ();
if ( wt . includes ( 'new building' )) return 'NB' ;
if ( wt . includes ( 'full demolition' )) return 'DM' ;
if ( wt . includes ( 'general construction' )) return 'GC' ;
if ( wt . includes ( 'plumbing' )) return 'PL' ;
if ( wt . includes ( 'mechanical' )) return 'ME' ;
if ( wt . includes ( 'solar' )) return 'SOL' ;
if ( wt . includes ( 'sidewalk shed' )) return 'SHD' ;
if ( wt . includes ( 'scaffold' )) return 'SCF' ;
if ( wt . includes ( 'construction fence' )) return 'FNC' ;
if ( wt . includes ( 'sign' )) return 'SG' ;
if ( wt . includes ( 'foundation' )) return 'FND' ;
if ( wt . includes ( 'structural' )) return 'STR' ;
if ( wt . includes ( 'boiler' )) return 'BLR' ;
if ( wt . includes ( 'sprinkler' )) return 'SPR' ;
if ( wt . includes ( 'earth work' )) return 'EW' ;
if ( wt . includes ( 'antenna' )) return 'ANT' ;
if ( wt . includes ( 'curb cut' )) return 'CC' ;
if ( wt . includes ( 'standpipe' )) return 'STP' ;
return 'OTH' ;
}
Example Mappings
Raw work_type String Normalized Code "General Construction"GC"GENERAL CONSTRUCTION (GC)"GC"Plumbing"PL"Mechanical Equipment"ME"Solar Photovoltaic - Residential"SOL"Sidewalk Shed - Public Safety"SHD"Scaffold"SCF"Construction Fence"FNC"Sign - Accessory"SG"Foundation"FND"Boiler Equipment"BLR"Fire Suppression - Sprinklers"SPR"Antenna"ANT"Curb Cut"CC"Standpipe"STP"Equipment Work"OTH
The workTypeToCode() function uses substring matching (includes()) to handle variations in capitalization, prefixes, and suffixes. This makes it robust to changes in the NYC Open Data schema.
Helper Functions
Get Color
export function getJobColor ( jobType : string ) : string {
return WORK_TYPE_COLORS [ jobType ] ?? '#666666' ;
}
// Usage
const color = getJobColor ( 'NB' ); // '#00ff88'
Get Emoji
export function getJobEmoji ( jobType : string ) : string {
return WORK_TYPE_EMOJIS [ jobType ] ?? 'π' ;
}
// Usage
const emoji = getJobEmoji ( 'DM' ); // 'π₯'
Get Label
export const WORK_TYPE_LABELS : Record < string , string > = {
NB: 'New Building' , DM: 'Demolition' , GC: 'General Construction' ,
PL: 'Plumbing' , ME: 'Mechanical' , SOL: 'Solar' ,
SHD: 'Sidewalk Shed' , SCF: 'Scaffold' , FNC: 'Const. Fence' ,
SG: 'Sign' , FND: 'Foundation' , STR: 'Structural' ,
BLR: 'Boiler' , SPR: 'Sprinklers' , EW: 'Earth Work' ,
ANT: 'Antenna' , CC: 'Curb Cut' , STP: 'Standpipe' ,
OTH: 'Other' ,
};
export function getJobLabel ( jobType : string ) : string {
return WORK_TYPE_LABELS [ jobType ] ?? jobType ;
}
// Usage
const label = getJobLabel ( 'SOL' ); // 'Solar'
Filtering Logic
The filter UI allows users to select which permit types to display:
const [ filters , setFilters ] = useState < FilterState >({
jobTypes: new Set ( ALL_JOB_TYPES ), // all types enabled by default
boroughs: new Set ([ 'MANHATTAN' ]),
daysBack: 7 ,
});
const filteredPermits = useMemo (() => permits . filter ( p => {
const jt = p . job_type ?. toUpperCase () ?? 'OTHER' ;
const jobTypeMatch = filters . jobTypes . has ( jt ) ||
( ! ALL_JOB_TYPES . includes ( jt ) && filters . jobTypes . has ( 'OTHER' ));
return jobTypeMatch && /* borough match */ ;
}), [ permits , filters ]);
Default Enabled Types
export const ALL_JOB_TYPES = [
'NB' , 'DM' , 'GC' , 'PL' , 'ME' , 'SOL' ,
'SHD' , 'SCF' , 'FNC' , 'STR' , 'FND' , 'SG'
];
These 12 types appear in the filter UI by default. Less common types (BLR, SPR, EW, ANT, CC, STP) are grouped under βOTHERβ.
Toggle Handler
const toggleJobType = ( jt : string ) => setFilters ( prev => {
const next = new Set ( prev . jobTypes );
next . has ( jt ) ? next . delete ( jt ) : next . add ( jt );
return { ... prev , jobTypes: next };
});
Permit Type Distribution
Based on typical 7-day Manhattan dataset (~2,000 permits):
Type Approx. % Example Count GC 35% ~700 PL 20% ~400 ME 15% ~300 SHD 10% ~200 SCF 8% ~160 SOL 5% ~100 NB 3% ~60 FND 2% ~40 DM 1% ~20 Other (combined) 1% ~20
General Construction (GC) dominates because it covers alterations, renovations, and interior work - the most common construction activity in NYC.
Marker Styling
On the map, each marker is color-coded and has hover effects:
.permit-marker {
width : 10 px ;
height : 10 px ;
background : var ( --color );
border-radius : 50 % ;
box-shadow : 0 0 6 px var ( --color ), 0 0 12 px var ( --color );
transition : opacity 0.15 s , transform 0.15 s ;
cursor : pointer ;
}
.permit-marker:hover {
transform : scale ( 1.5 );
opacity : 1 !important ;
z-index : 1000 ;
}
.permit-marker--selected {
box-shadow : 0 0 12 px var ( --color ), 0 0 24 px var ( --color ), 0 0 36 px #fff ;
transform : scale ( 1.3 );
z-index : 999 ;
}
The --color CSS custom property is set dynamically:
markerElement . style . setProperty ( '--color' , getJobColor ( permit . job_type ?? '' ));
Next Steps
Architecture See how permit types integrate with the component system
Data Sources Learn where permit type data comes from