Need a Licensed Land Surveyor in Tamil Nadu?Get a free quote on WhatsApp - we usually reply within 15 minutes.★ 4.9 rating  ·  600+ Google reviews  ·  9+ years experienceதமிழ்நாட்டில் உரிமம் பெற்ற நில அளவையாளர் தேவையா?வாட்ஸ்அப்பில் இலவச விலை மதிப்பீடு பெறுங்கள் - 15 நிமிடங்களில் பதிலளிப்போம்.★ 4.9 மதிப்பீடு  ·  600+ கூகுள் மதிப்புரைகள்  ·  9+ ஆண்டுகள் அனுபவம்
FMB Sketch Drawing

FMB Drawing Tool — Create an FMB Sketch Online, Free

If you have a field book full of G-line distances and offsets, you can now turn them into a finished FMB drawing without touching AutoCAD. The FMB drawing tool from SELVI Land Surveyors runs in your browser: type the measurements exactly as they were taken, and the tool draws the boundary, calculates the area in m², sq.ft, cent, acre and hectare, and exports a clean sheet as PDF, DXF, Excel or PNG.

Nothing to install, no login, and no measurement ever leaves your device.

FMB Studio

SELVI Land Surveyors · Drawing workspace

Measured data. Clear drawings.

Enter your measurements below, check the preview, then export. All distances are in metres.

Sample drawing loaded — replace it with your survey data or choose Clear All.
Ready · Edit measurements to begin.
DRAWING PAPER PREVIEW
FMB Sheet Preview
"); w.document.close(); w.addEventListener("load",()=>{w.focus();w.print()},{once:true}); } function updatePaper(){ $("sheet").className=`paper ${$("paper").value.toLowerCase()} ${$("orientation").value}`; root.querySelectorAll(".paper").forEach(pg=>{pg.style.transform=`scale(${zoom})`}); $("sheet").style.transformOrigin="center top"; } function project(){ return { format:"FMB-STUDIO-V12", details:{ brand:"SELVI Land Surveyors",tagline:"Property Experts",contact:"9655196554", district:$("district").value,taluk:$("taluk").value,village:$("village").value, villageCode:$("vcode").value,survey:$("survey").value,paper:$("paper").value, orientation:$("orientation").value,scale:$("scale").value,rotation:$("rotation").value, surveyor:$("surveyor").value,date:$("date").value,sheetNo:$("sheetNo").value,language:LANG }, ladder:ladders(),connectors:conns(),angles:angles(),subdivisions:subs(),areas:areaRows(), pointsOnLines:pointsOnLines(),divisionRules:divisionRows(),removeRules:removeRules(), display:Object.fromEntries(['showLadderTb','showLadderPg','showAreaTb','showDiv'].map(id=>[id,$(id).checked])) }; } function download(blob,name){ if(!blob)return; try{ if(window.navigator && navigator.msSaveOrOpenBlob){navigator.msSaveOrOpenBlob(blob,name);return} const url=URL.createObjectURL(blob),a=document.createElement("a"); a.href=url;a.download=name;a.rel="noopener";a.style.position="fixed";a.style.left="-9999px"; document.body.appendChild(a);a.click(); setTimeout(()=>{URL.revokeObjectURL(url);a.remove()},1500); }catch(err){ console.error("Download failed:",err); alert("Download failed: "+(err.message||err)); } } function saveJSON(){ const data=JSON.stringify(project(),null,2); download(new Blob([data],{type:"application/json;charset=utf-8"}),"FMB-Project.json"); }function crc32(bytes){ let c=0xffffffff; for(const b of bytes){c^=b;for(let k=0;k<8;k++)c=(c>>>1)^((c&1)?0xedb88320:0)} return (c^0xffffffff)>>>0; } function u16(n){return new Uint8Array([n&255,(n>>>8)&255])} function u32(n){return new Uint8Array([n&255,(n>>>8)&255,(n>>>16)&255,(n>>>24)&255])} function concatBytes(parts){let n=0;parts.forEach(p=>n+=p.length);const o=new Uint8Array(n);let k=0;parts.forEach(p=>{o.set(p,k);k+=p.length});return o} function zipStore(files){ const enc=new TextEncoder(),local=[],central=[];let offset=0; for(const f of files){ const name=enc.encode(f.name),data=typeof f.data==="string"?enc.encode(f.data):f.data,crc=crc32(data); const h=concatBytes([new Uint8Array([80,75,3,4,20,0,0,0,0,0,0,0,0,0]),u32(crc),u32(data.length),u32(data.length),u16(name.length),u16(0),name]); local.push(h,data);central.push(concatBytes([new Uint8Array([80,75,1,2,20,0,20,0,0,0,0,0,0,0]),u32(crc),u32(data.length),u32(data.length),u16(name.length),u16(0),u16(0),u16(0),u16(0),u32(0),u32(offset),name])); offset+=h.length+data.length; } const cd=concatBytes(central),body=concatBytes(local); const end=concatBytes([new Uint8Array([80,75,5,6,0,0,0,0,...u16(files.length),...u16(files.length)]),u32(cd.length),u32(body.length),u16(0)]); return concatBytes([body,cd,end]); } function xlsxCellRef(r,c){return String.fromCharCode(65+(c%26))+(r+1)} function sheetXml(rows){ const esc=v=>String(v??"").replace(/&/g,"&").replace(//g,">"); let x=''; rows.forEach((row,r)=>{if(!row||!row.length)return;x+='';row.forEach((v,c)=>{const val=esc(v);if(val==="")return;x+=''+val+''});x+=""}); return x+""; } function excelRows(p,A,areas){ const ladderRows=[ ["SELVI Land Surveyors"],["Property Experts"],["TOTAL STATION \u2022 DGPS \u2022 DRONE"],["Mobile : 9655196554"],[],["DETAILS"], ["District",p.details.district],["Taluk",p.details.taluk],["Village",p.details.village],["Village Code",p.details.villageCode],["Survey No.",p.details.survey], ["Paper",p.details.paper],["Orientation",p.details.orientation],["Scale",p.details.scale],["Overall Area Ha",A.ha],["Overall Area a",A.are] ]; const ladderSheet=[ ["Bend Name","Left Offset (m)","Line","Right Offset (m)","Bend Name"], ...p.ladder.map(r=>[r.bend1,r.left??"",r.line,r.right??"",r.bend2]) ]; const connSheet=[ ["Type","Start","End"], ...p.connectors.map(r=>[r.type,r.start,r.end]) ]; const angleSheet=[ ["Angle","From","Vertex","To"], ...p.angles.map(r=>[r.name,r.from,r.vertex,r.to]) ]; const subSheet=[ ["Name","Parent","Sequence"], ...p.subdivisions.map(r=>[r.name,r.parent,r.seq]) ]; const areaSheet=[ ["Area Name","Point Sequence"], ...p.areas.map(r=>[r.name,r.seq]) ]; return [{name:"Details",rows:ladderRows},{name:"Ladder",rows:ladderSheet},{name:"Connectors",rows:connSheet},{name:"Angles",rows:angleSheet},{name:"Sub-Divisions",rows:subSheet},{name:"Areas",rows:areaSheet}]; } async function xlsxWrite(sheets,fileName){ const enc=new TextEncoder(); const deflate=async raw=>new Uint8Array(await new Response(new Blob([raw]).stream().pipeThrough(new CompressionStream("deflate-raw"))).arrayBuffer()); const crcTable=(()=>{const t=[];for(let n=0;n<256;n++){let c=n;for(let k=0;k<8;k++)c=(c&1)?0xedb88320^(c>>>1):c>>>1;t[n]=c>>>0}return t})(); const crc=b=>{let c=0xffffffff;for(const x of b)c=crcTable[(c^x)&255]^(c>>>8);return(c^0xffffffff)>>>0}; const u16=n=>new Uint8Array([n&255,(n>>>8)&255]),u32=n=>new Uint8Array([n&255,(n>>>8)&255,(n>>>16)&255,(n>>>24)&255]); const cat=parts=>{let n=parts.reduce((s,p)=>s+p.length,0),o=new Uint8Array(n),k=0;parts.forEach(p=>{o.set(p,k);k+=p.length});return o}; const esc=v=>String(v??"").replace(/&/g,"&").replace(//g,">"); const files={}; files["[Content_Types].xml"]=''+sheets.map((s,i)=>'').join("")+''; files["_rels/.rels"]=''; files["xl/workbook.xml"]=''+sheets.map((s,i)=>'').join("")+''; files["xl/_rels/workbook.xml.rels"]=''+sheets.map((s,i)=>'').join("")+''; sheets.forEach((s,i)=>{files["xl/worksheets/sheet"+(i+1)+".xml"]=sheetXml(s.rows)}); const local=[],central=[];let offset=0; for(const[name,text]of Object.entries(files)){ const raw=enc.encode(text),comp=await deflate(raw),nb=enc.encode(name),c=crc(raw); local.push(cat([u32(0x04034b50),u16(20),u16(0),u16(8),u16(0),u16(0),u32(c),u32(comp.length),u32(raw.length),u16(nb.length),u16(0),nb,comp])); central.push(cat([u32(0x02014b50),u16(20),u16(20),u16(0),u16(8),u16(0),u16(0),u32(c),u32(comp.length),u32(raw.length),u16(nb.length),u16(0),u16(0),u16(0),u16(0),u32(0),u32(offset),nb])); offset+=local[local.length-1].length; } const body=cat(local),cd=cat(central),eocd=cat([u32(0x06054b50),u16(0),u16(0),u16(central.length),u16(central.length),u32(cd.length),u32(body.length),u16(0)]); download(new Blob([body,cd,eocd],{type:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}),fileName); } function saveExcel(){ const p=project(),A=calcArea(),areas=calcNamedAreas().filter(x=>x.closed); const sheets=excelRows(p,A,areas); if(!window.CompressionStream){alert("This browser does not support XLSX export. Use current Chrome or Edge.");return} xlsxWrite(sheets,"FMB-Project.xlsx").catch(e=>{console.error(e);alert("Excel export failed: "+e.message)}); } function saveSampleExcel(){ const sample={details:{district:"Thiruvarur",taluk:"Nannilam",village:"Nannilam",villageCode:"49",survey:"123/1",paper:"A4",orientation:"portrait",scale:"500"}, ladder:[{bend1:"",left:null,line:"A",right:null,bend2:""},{bend1:"",left:null,line:"(100)",right:null,bend2:""},{bend1:"1",left:10,line:"50",right:null,bend2:""},{bend1:"",left:null,line:"B",right:null,bend2:""},{bend1:"",left:null,line:"(50)",right:null,bend2:""},{bend1:"",left:null,line:"25",right:15,bend2:"2"},{bend1:"",left:null,line:"C",right:null,bend2:""},{bend1:"",left:null,line:"(80)",right:null,bend2:""},{bend1:"",left:null,line:"A",right:null,bend2:""}], connectors:[{type:"Field Line",start:"A",end:"1"},{type:"Field Line",start:"1",end:"2"},{type:"Field Line",start:"2",end:"C"},{type:"Field Line",start:"C",end:"B"},{type:"Field Line",start:"B",end:"A"}], angles:[],subdivisions:[{name:"1",parent:"Main",seq:"A,1,2,C"}],areas:[{name:"Sub-Division 1",seq:"A,1,2,C"}]}; const sheets=excelRows(sample,{closed:true,m2:277.181,are:2.772,ha:0.0277},[]); if(!window.CompressionStream){alert("Excel export needs a current browser.");return} xlsxWrite(sheets,"FMB-Sample.xlsx").catch(e=>{console.error(e);alert("Sample export failed: "+e.message)}); } async function inflateRaw(bytes){ const ds=new DecompressionStream("deflate-raw"); return new Uint8Array(await new Response(new Blob([bytes]).stream().pipeThrough(ds)).arrayBuffer()); } function zipEntries(buf){ const dv=new DataView(buf),u8=new Uint8Array(buf);let eocd=-1; for(let i=u8.length-22;i>=0;i--){if(u8[i]===0x50&&u8[i+1]===0x4b&&u8[i+2]===0x05&&u8[i+3]===0x06){eocd=i;break}} if(eocd<0)throw new Error("Not a valid Excel (ZIP) file"); const cdOff=dv.getUint32(eocd+16,true),cdCount=dv.getUint16(eocd+10,true),out={};let p=cdOff; for(let i=0;i{let col=-1;for(const ch of String(ref||"")){if(ch>="A"&&ch<="Z")col=col<0?0:col;if(col>=0&&ch>="A"&&ch<="Z")col=col*26+(ch.charCodeAt(0)-64);else if(col>=0)break}return col>=0?col-1:-1}; doc.querySelectorAll("row").forEach(row=>{ const cells=[];let max=0; row.querySelectorAll("c").forEach(c=>{ const ref=c.getAttribute("r")||"",col=colOf(ref)>=0?colOf(ref):cells.length; let val="";const t=c.getAttribute("t"),v=c.querySelector("v"); if(t==="inlineStr")val=(c.querySelector("is t")||{textContent:""}).textContent||""; else if(t==="s"&&v)val=shared[+v.textContent]||""; else if(v)val=v.textContent||""; while(cells.lengthmax)max=col; }); cells.length=max+1;rows.push(cells); }); return rows; } async function importExcelFile(file){ const buf=await file.arrayBuffer(),files=await zipRead(buf); const dec=new TextDecoder();let shared=[]; if(files["xl/sharedStrings.xml"]){const d=new DOMParser().parseFromString(dec.decode(files["xl/sharedStrings.xml"]),"application/xml");d.querySelectorAll("si").forEach(si=>shared.push(si.textContent||""))} const wb=new DOMParser().parseFromString(dec.decode(files["xl/workbook.xml"]),"application/xml"); const names=[...wb.querySelectorAll("sheet")].map(s=>s.getAttribute("name")||("Sheet"+(s.getAttribute("sheetId")||""))); const sheets=[];names.forEach((nm,idx)=>{const key="xl/worksheets/sheet"+(idx+1)+".xml";if(files[key])sheets.push({name:nm,rows:xmlRowsToArray(dec.decode(files[key]),shared)})}); const L=(x="")=>String(x??"").trim().toLowerCase(); const findHeader=(rows,headers)=>{for(let i=0;ir[k]&&r[k].indexOf(h)>=0))return i}return -1}; const details=sheets.flatMap(s=>s.rows).filter(r=>r[0]&&/^(district|taluk|village|village code|survey)/i.test(String(r[0]).trim())); details.forEach(r=>{const k=L(r[0]),v=String(r[1]??"").trim();if(k==="district"&&$("district"))$("district").value=v;else if(k==="taluk"&&$("taluk"))$("taluk").value=v;else if(k==="village"&&$("village"))$("village").value=v;else if(k==="village code"&&$("vcode"))$("vcode").value=v;else if(k==="survey"&&$("survey"))$("survey").value=v;else if(k==="paper"&&$("paper"))$("paper").value=v;else if(k==="orientation"&&$("orientation"))$("orientation").value=v;else if(k==="scale"&&$("scale"))$("scale").value=v}); const setRows=(tbId,header,mapFn)=>{ for(const sh of sheets){ const h=findHeader(sh.rows,header);if(h<0)continue; const tb=$(tbId),tbody=tb.querySelector("tbody");tbody.innerHTML=""; for(let i=h+1;iString(x).trim()!==""))break;const d=mapFn(r);if(!d)continue;if(tbId==="ladder")addL(d);else if(tbId==="conn")addC(d);else if(tbId==="angles")addA(d);else if(tbId==="subs")addS(d);else if(tbId==="areas")addArea(d)} return true; } return false; }; setRows("ladder",["bend name","left offset","line","right offset","bend name"],r=>({bend1:r[0],left:r[1]===""?null:Number(r[1]),line:r[2],right:r[3]===""?null:Number(r[3]),bend2:r[4]})); setRows("conn",["type","start","end"],r=>({type:r[0],start:r[1],end:r[2]})); setRows("angles",["angle","from","vertex","to"],r=>({name:r[0],from:r[1],vertex:r[2],to:r[3]})); setRows("subs",["name","parent","sequence"],r=>({name:r[0],parent:r[1],seq:r[2]})); setRows("areas",["area name","point sequence"],r=>({name:r[0],seq:r[1]})); render(); }function buildDXF(n, connectors, pol){ const valid=p=>p&&Number.isFinite(p.x)&&Number.isFinite(p.y), all=Object.values(n.map); if(!all.length)throw new Error('Enter measured drawing points before exporting DXF.'); if(all.some(p=>!valid(p)))throw new Error('A point has invalid coordinates. Check your measurements.'); const missing=connectors.filter(c=>!n.map[c.start]||!n.map[c.end]); if(missing.length)throw new Error('Unresolved connector: '+missing.map(c=>c.start+' to '+c.end).join(', ')); if(pol.some(p=>!n.map[p.name]))throw new Error('A point on a line could not be placed. Check its From / To references.'); const out=[],pair=(code,value)=>out.push(String(code),String(value)); const ascii=value=>{const t=String(value).replace(/[\r\n\t]/g,' ');if(/[^\x20-\x7e]/.test(t))throw new Error('Use English letters, numbers or ASCII symbols for DXF point labels. Other-language project details remain in JSON and the printed sheet.');return t}; const num=v=>{if(!Number.isFinite(v)||Math.abs(v)>1e12)throw new Error('Invalid or excessively large DXF coordinate.');return v.toFixed(8)}; const xyz=(p,b=10)=>{pair(b,num(p.x));pair(b+10,num(p.y));pair(b+20,0)}; const minX=Math.min(...all.map(p=>p.x)),minY=Math.min(...all.map(p=>p.y)),maxX=Math.max(...all.map(p=>p.x)),maxY=Math.max(...all.map(p=>p.y)); // R12: header variables are code 9 / name pairs. One model-space unit = one metre. pair(999,'FMB Studio | Model space: 1 drawing unit = 1 metre'); pair(0,'SECTION');pair(2,'HEADER');pair(9,'$ACADVER');pair(1,'AC1009'); pair(9,'$LUNITS');pair(70,2);pair(9,'$LUPREC');pair(70,4);pair(9,'$INSBASE');xyz({x:0,y:0}); pair(9,'$EXTMIN');xyz({x:minX,y:minY});pair(9,'$EXTMAX');xyz({x:Math.max(maxX,minX+150),y:maxY+14}); pair(9,'$PDMODE');pair(70,3);pair(9,'$PDSIZE');pair(40,.6);pair(0,'ENDSEC'); pair(0,'SECTION');pair(2,'TABLES');pair(0,'TABLE');pair(2,'LTYPE');pair(70,1); pair(0,'LTYPE');pair(2,'CONTINUOUS');pair(70,0);pair(3,'Solid line');pair(72,65);pair(73,0);pair(40,0);pair(0,'ENDTAB'); const layers=[['0',7],['FIELD_LINE',7],['G_LINE',1],['SUBDIVISION',8],['OFFSET',3],['POINT_LABEL',1],['BEND_LABEL',5],['BRAND',7]]; pair(0,'TABLE');pair(2,'LAYER');pair(70,layers.length); layers.forEach(([name,color])=>{pair(0,'LAYER');pair(2,name);pair(70,0);pair(62,color);pair(6,'CONTINUOUS')});pair(0,'ENDTAB'); pair(0,'TABLE');pair(2,'STYLE');pair(70,1);pair(0,'STYLE');pair(2,'STANDARD');pair(70,0);pair(40,0);pair(41,1);pair(50,0);pair(71,0);pair(42,2.5);pair(3,'txt');pair(4,'');pair(0,'ENDTAB');pair(0,'ENDSEC'); pair(0,'SECTION');pair(2,'BLOCKS');pair(0,'ENDSEC');pair(0,'SECTION');pair(2,'ENTITIES'); let lines=0; const line=(a,b,layer)=>{if(!valid(a)||!valid(b))throw new Error('A drawing line has missing coordinates.');pair(0,'LINE');pair(8,layer);xyz(a);xyz(b,11);lines++}; const text=(p,label,layer,h)=>{pair(0,'TEXT');pair(8,layer);xyz(p);pair(40,num(h));pair(1,ascii(label));pair(7,'STANDARD')}; const point=(p,label,layer,h)=>{pair(0,'POINT');pair(8,layer);xyz(p);text({x:p.x+.8,y:p.y+.8},label,layer,h)}; n.g.segments.forEach(seg=>{if(Number.isFinite(seg.distance))line(n.map[seg.from],n.map[seg.to],'G_LINE')}); connectors.forEach(c=>line(n.map[c.start],n.map[c.end],c.type==='Field Line'?'FIELD_LINE':'SUBDIVISION')); n.bp.forEach(p=>{const seg=n.g.segments.find(s=>s.from===p.root);if(seg)line(at(n.map[seg.from],n.map[seg.to],p.d),p,'OFFSET')}); Object.entries(n.g.points).forEach(([name,p])=>point(p,name,'POINT_LABEL',2.5));n.bp.forEach(p=>{if(p.name)point(p,p.name,'BEND_LABEL',2)});pol.forEach(p=>point(n.map[p.name],p.name,'BEND_LABEL',2)); if(!lines)throw new Error('No measured lines are available to export.'); text({x:minX,y:maxY+8},'SELVI Land Surveyors - Mobile: 9655196554','BRAND',4); pair(0,'ENDSEC');pair(0,'EOF');return out.join('\r\n')+'\r\n'; } function saveDXF(){try{const n=named();if(n.g.message&&!confirm('Drawing warning: '+n.g.message+'\n\nExport this drawing anyway?'))return;const data=buildDXF(n,conns(),pointsOnLines());download(new Blob([data],{type:'application/dxf'}),'FMB-Sketch.dxf');announce('DXF download requested · AutoCAD R12 · 1 drawing unit = 1 metre.')}catch(err){announce('DXF not exported: '+err.message,true)}}function savePDF(){ render(); const vb=$("svg").viewBox.baseVal; const ps=$("sheet").className.includes("a2")?"A2":($("sheet").className.includes("a3")?"A3":"A4"); const landscape=$("sheet").className.includes("landscape"); const W=ps==="A4"?(landscape?841.89:595.28):ps==="A3"?(landscape?1190.55:841.89):(landscape?1683.78:1190.55); const H=ps==="A4"?(landscape?595.28:841.89):ps==="A3"?(landscape?841.89:1190.55):(landscape?1190.55:1683.78); const sx=W/vb.width,sy=H/vb.height,cmd=[]; const esc=s=>String(s).replace(/\\/g,"\\\\").replace(/\(/g,"\\(").replace(/\)/g,"\\)"); const rgb=c=>{const m=String(c||"#111").match(/#([0-9a-f]{6})/i);if(!m)return[0,0,0];return[parseInt(m[1].slice(0,2),16)/255,parseInt(m[1].slice(2,4),16)/255,parseInt(m[1].slice(4,6),16)/255]}; const X=x=>x*sx,Y=y=>H-y*sy; let imgUsed=false; const walk=node=>{ if(node.nodeType!==1)return; const t=node.tagName.toLowerCase(); if(t==="line"){ const c=rgb(node.getAttribute("stroke")),w=(+node.getAttribute("stroke-width")||1)*((sx+sy)/2); const d=node.getAttribute("stroke-dasharray"); cmd.push(`${c[0]} ${c[1]} ${c[2]} RG ${Math.max(.2,w).toFixed(3)} w ${d?`[${d.split(/\s+/).map(z=>(+z*((sx+sy)/2)).toFixed(2)).join(" ")}] 0 d`:"[] 0 d"} ${X(+node.getAttribute("x1")).toFixed(2)} ${Y(+node.getAttribute("y1")).toFixed(2)} m ${X(+node.getAttribute("x2")).toFixed(2)} ${Y(+node.getAttribute("y2")).toFixed(2)} l S`); }else if(t==="text"){ const c=rgb(node.getAttribute("fill")),fs=(+node.getAttribute("font-size")||3)*((sx+sy)/2),txtv=esc(node.textContent||""); cmd.push(`${c[0]} ${c[1]} ${c[2]} rg BT /F1 ${fs.toFixed(2)} Tf ${X(+node.getAttribute("x")||0).toFixed(2)} ${Y(+node.getAttribute("y")||0).toFixed(2)} Td (${txtv}) Tj ET`); }else if(t==="rect"&&node.getAttribute("stroke")){ const c=rgb(node.getAttribute("stroke")),w=(+node.getAttribute("stroke-width")||1)*((sx+sy)/2),x=+node.getAttribute("x")||0,y=+node.getAttribute("y")||0,ww=+node.getAttribute("width")||0,hh=+node.getAttribute("height")||0; cmd.push(`${c[0]} ${c[1]} ${c[2]} RG ${Math.max(.2,w).toFixed(3)} w ${X(x).toFixed(2)} ${Y(y+hh).toFixed(2)} ${X(ww).toFixed(2)} ${Y(y)-Y(y+hh)} re S`); }else if(t==="path"){ const fill=node.getAttribute("fill"); if(fill&&fill!=="none"){ const m=node.getAttribute("d").match(/M\s*([-\d.]+)\s+([-\d.]+).*L\s*([-\d.]+)\s+([-\d.]+).*L\s*([-\d.]+)\s+([-\d.]+).*Z/); if(m){const c=rgb(fill);cmd.push(`${c[0]} ${c[1]} ${c[2]} rg ${X(+m[1]).toFixed(2)} ${Y(+m[2]).toFixed(2)} m ${X(+m[3]).toFixed(2)} ${Y(+m[4]).toFixed(2)} l ${X(+m[5]).toFixed(2)} ${Y(+m[6]).toFixed(2)} l h f`)} } }else if(t==="image"){ const x=+node.getAttribute("x")||0,y=+node.getAttribute("y")||0,w=+node.getAttribute("width")||0,h=+node.getAttribute("height")||0; cmd.push(`q ${(w*sx).toFixed(2)} 0 0 ${(h*sy).toFixed(2)} ${X(x).toFixed(2)} ${Y(y+h).toFixed(2)} cm /Im1 Do Q`); imgUsed=true; } [...node.children].forEach(walk); }; walk($("svg")); const stream=cmd.join("\n")+"\n"; const enc=new TextEncoder(); const jpgB64=FMB_LOGO_JPG.split(",")[1],bin=atob(jpgB64); const jpg=new Uint8Array(bin.length);for(let i=0;i>`, `<< /Type /Pages /Kids [3 0 R] /Count 1 >>`, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${W.toFixed(2)} ${H.toFixed(2)}] /Resources << /Font << /F1 5 0 R >> ${imgUsed?"/XObject << /Im1 6 0 R >> ":""}>> /Contents 4 0 R >>`, `<< /Length ${enc.encode(stream).length} >>\nstream\n${stream}endstream`, `<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>` ]; const objCount=imgUsed?6:5; const out=[];let off=0; const emit=u=>{out.push(u);off+=u.length;return off-u.length}; const offsets=[0]; emit(enc.encode("%PDF-1.4\n")); textObjs.forEach((o,i)=>{offsets[i+1]=emit(enc.encode(`${i+1} 0 obj\n${o}\nendobj\n`))}); if(imgUsed){ const pre=enc.encode(`6 0 obj\n<< /Type /XObject /Subtype /Image /Width 480 /Height 237 /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length ${jpg.length} >>\nstream\n`); const suf=enc.encode(`\nendstream\nendobj\n`); const cat=new Uint8Array(pre.length+jpg.length+suf.length); cat.set(pre,0);cat.set(jpg,pre.length);cat.set(suf,pre.length+jpg.length); offsets[6]=emit(cat); } const xref=off; emit(enc.encode(`xref\n0 ${objCount+1}\n0000000000 65535 f \n`)); for(let i=1;i<=objCount;i++)emit(enc.encode(String(offsets[i]).padStart(10,"0")+" 00000 n \n")); emit(enc.encode(`trailer << /Size ${objCount+1} /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF`)); download(new Blob([concatBytes(out)],{type:"application/pdf"}),"FMB-Sketch.pdf"); } $("addL").onclick=()=>{addL();render()};$("addArea").onclick=()=>{addArea();render()};$("addC").onclick=()=>{addC();render()};$("addA").onclick=()=>{addA();render()};$("addS").onclick=()=>{addS();render()};$("refresh").onclick=render;$("json").onclick=saveJSON;$("print").onclick=()=>window.print(); function gateExport(key,fn){ try{ if(["png","pdf","dxf","excel"].indexOf(key)>=0){ const errs=validateIssues().filter(i=>i.level==="error"); if(errs.length){ const list=errs.slice(0,5).map(i=>"• "+i.text).join("\n"); if(!confirm(T("There are {n} problems to check before export:",{n:errs.length})+"\n\n"+list+"\n\n"+T("Export anyway?"))){ gotoStep(4);announce(T("Export cancelled · fix the checks first."),true);return; } } } }catch(e){console.error("check gate:",e)} fn(); } $("dxf").onclick=()=>gateExport("dxf",saveDXF); $("pdf").onclick=()=>gateExport("pdf",savePDF); $("excel").onclick=()=>gateExport("excel",saveExcel); $("sampleSub").onclick=()=>{addS({name:"1",parent:"Main",seq:"A,1,2,C"});render()}; $("sampleArea").onclick=()=>{addArea({name:"Sub-Division 1",seq:"A,1,2,C"});render()}; $("sampleAngle").onclick=()=>{addA({name:"ACB",from:"A",vertex:"C",to:"B"});render()}; $("showLadderPg").addEventListener("change",render); $("showAreaTb").addEventListener("change",render); $("showLadderTb").addEventListener("change",render); $("showDiv").addEventListener("change",render);$("addDiv").onclick=()=>{addDivRow();render()}; $("addRm").onclick=()=>{addRmRow();render()}; $("addPol").onclick=()=>{addPolRow();render()}; $("minus").onclick=()=>{zoom=Math.max(.5,zoom-.15);root.querySelectorAll(".paper").forEach(pg=>pg.style.transform=`scale(${zoom})`)};$("plus").onclick=()=>{zoom=Math.min(2.2,zoom+.15);root.querySelectorAll(".paper").forEach(pg=>pg.style.transform=`scale(${zoom})`)};$("reset").onclick=()=>{zoom=1;root.querySelectorAll(".paper").forEach(pg=>pg.style.transform="scale(1)")}; $("upload").onclick=()=>gateExport("upload",()=>$("file").click()); $("xlsx").onclick=()=>$("fileXlsx").click(); $("clear").onclick=()=>{ if(!confirm("Clear all project measurements and details? Save JSON first if you need a backup."))return; ["district","taluk","village","vcode","survey","scale","rotation","surveyor","date","sheetNo"].forEach(id=>{const el=$(id);if(!el)return;el.value=id==="scale"?"500":id==="rotation"?"0":id==="surveyor"?"SELVI Land Surveyors":id==="sheetNo"?"1":id==="date"?localDate():""}); ["ladder","conn","angles","subs","areas","polTb","divTb","rmTb"].forEach(id=>{const tb=$(id).querySelector("tbody");tb.innerHTML=""}); render(); snapshot();autosaveNow(); }; $("sample").onclick=saveSampleExcel; $("fileXlsx").onchange=e=>{const f=e.target.files[0];if(!f)return;importExcelFile(f).then(()=>{e.target.value=""}).catch(err=>{console.error(err);alert("Excel import failed: "+err.message)})}; $("file").onchange=e=>{ const f=e.target.files[0]; if(!f)return; const rd=new FileReader(); rd.onload=()=>{ try{ applyProject(JSON.parse(rd.result)); }catch(err){console.error(err);alert("Invalid FMB JSON project: "+err.message)} }; rd.readAsText(f); e.target.value=""; }; ["district","taluk","village","vcode","survey","paper","orientation","scale","rotation","surveyor","date","sheetNo"].forEach(id=>{const el=$(id);if(el)el.addEventListener("input",()=>{zoom=1;render()})});function announce(message,error=false){const el=$('actionStatus');el.textContent=message;el.classList.toggle('bad',error)} $('refresh').removeAttribute('style');$('refresh').classList.add('primary'); $('refresh').onclick=()=>{render();announce($('status').textContent,$('status').classList.contains('bad'))}; $('quickUpdate').onclick=()=>$('refresh').click(); $('quickPreview').onclick=()=>root.querySelector('.preview').scrollIntoView({behavior:'smooth',block:'start'}); $('quickDXF').onclick=saveDXF; $('quickPNG').onclick=()=>gateExport("png",savePNG); $('png').onclick=()=>gateExport("png",savePNG); $('undo').onclick=undo;$('redo').onclick=redo;$('lang').onclick=toggleLanguage;$('fit').onclick=fitPreview; $('stepPrev').onclick=()=>gotoStep(STEP-1);$('stepNext').onclick=()=>gotoStep(STEP+1); root.querySelectorAll('.step-chip').forEach(c=>c.onclick=()=>gotoStep(Number(c.dataset.step))); $('autosaveRestore').onclick=()=>{const bar=$('autosaveBar'),raw=bar.dataset.pending||lsGet(V12_LS_KEY);try{applyProject(JSON.parse(raw).data,true);announce(T("Draft restored."))}catch(e){announce(String(e.message||e),true)}bar.hidden=true}; $('autosaveDiscard').onclick=()=>{lsDel(V12_LS_KEY);const bar=$('autosaveBar');bar.hidden=true;delete bar.dataset.pending;announce(T("Draft discarded."))}; window.FMB_I18N={t:T,apply:applyLanguage,get lang(){return LANG}}; // Small read-only hook so the tool can be inspected/extended (e.g. from WPCode). window.FMB_STUDIO={version:"12",project,snapshot,validate:validateIssues,applyProject,gotoStep,get lang(){return LANG}}; $('minus').setAttribute('aria-label','Zoom out');$('plus').setAttribute('aria-label','Zoom in');$('reset').setAttribute('aria-label','Reset drawing zoom'); root.querySelectorAll('.grid label').forEach(label=>{const c=label.parentElement.querySelector('input,select');if(c)label.htmlFor=c.id}); function labelControls(){ root.querySelectorAll('button').forEach(b=>{b.type='button';if(b.textContent.trim()==='×')b.setAttribute('aria-label','Delete row');if(b.textContent.trim()==='+')b.setAttribute('aria-label','Insert row above')}); root.querySelectorAll('table').forEach(table=>{const h=[...table.querySelectorAll('thead th')].map(t=>t.textContent.trim());table.querySelectorAll('tbody tr').forEach((tr,index)=>{[...tr.cells].forEach((td,i)=>td.querySelectorAll('input,select').forEach(c=>{c.setAttribute('aria-label',(h[i]||'Value')+', row '+(index+1));if(c.type==='number'){c.inputMode='decimal';c.step='any'}}))})}); } new MutationObserver(labelControls).observe(root.querySelector('.side'),{childList:true,subtree:true}); root.addEventListener('input',()=>{const demo=root.querySelector('.demo-note');if(demo)demo.hidden=true;queueHistory()}); root.addEventListener('change',queueHistory); root.addEventListener('keydown',e=>{const k=(e.key||'').toLowerCase();if(!(e.ctrlKey||e.metaKey))return;if(k==='z'&&!e.shiftKey){e.preventDefault();undo()}else if(k==='y'||(k==='z'&&e.shiftKey)){e.preventDefault();redo()}}); $('print').onclick=printSheet; labelControls();// v12: fill today's date before the first draw so the sheet title block shows it. if(!$("date").value)$("date").value=localDate();// Deterministic demonstration: A-(100)-B-(50)-C-(80)-A triangle with two offset points. [{line:"A"},{line:"(100)"},{bend1:"1",left:10,line:"50"},{line:"B"},{line:"(50)"},{line:"25",right:15,bend2:"2"},{line:"C"},{line:"(80)"},{line:"A"}].forEach(addL); [{start:"A",end:"1"},{start:"1",end:"2"},{start:"2",end:"C"},{start:"C",end:"B"},{start:"B",end:"A"}].forEach(addC); addS({name:"1",parent:"Main",seq:"A,1,2,C"}); addArea({name:"Sub-Division 1",seq:"A,1,2,C"}); addDivRow({from:"A",to:"B",parts:"2",dir:"Parallel"}); render();/* ---- v12 init ---- */ if(lsGet(V12_LANG_KEY)==="ta")LANG="ta"; applyLanguage(); gotoStep(1,false); lastSnapshot=JSON.stringify(project()); updateUndoButtons(); offerDraftRestore();})();

What is an FMB drawing?

An FMB (Field Measurement Book) drawing is the sketch the Revenue Department keeps for every survey number. It shows the shape of the plot, the G-line measurements along each side, the offsets taken to the boundary, and the total area — usually written in the traditional hectare–are (Ha-a) format. FMB sketches are used for land purchase, partition between family members, patta transfer, bank loans, layout planning and boundary disputes.

The official FMB is issued by your Taluk office. This tool helps you prepare the drawing — the sketch and area statement you need to check a survey number, to explain a case, or to attach to a submission — from measurements you have taken yourself.

How to draw an FMB sketch online — 4 simple steps

Step 1: Fill in the project details

District, taluk, village, village code, survey number, paper size (A4, A3 or A2), orientation, scale, surveyor name, date and sheet number. Everything you type here appears in the title block of the printed sheet, so the drawing is ready for a file.

Step 2: Enter your measurements

The ladder table takes your field book as it is: G points (A, B, C…), the line distance in round brackets, chainages, and left/right offsets with their bend names. Points that fall on a line can be added separately with their distance from a known point, and a numeric name such as 1, 2 or 5 can then be used anywhere in the drawing.

Step 3: Set the boundary, sub-divisions and areas

Type the boundary order as a sequence — for example A → 1 → 2 → C → B → A. A closed field line gives you the area; sub-division lines are drawn lighter. You can also add angle constraints (the angle is calculated from the geometry, never typed), named area sequences, and area dividing rules that split the plot into equal parts, parallel or perpendicular to a base line.

Step 4: Check and export

Before you export, the tool lists anything that is still missing — an undefined point, a distance written outside brackets, a broken sequence, an open boundary. Fix them, then download the drawing.

What the FMB tool works out for you

  • Area in five units — m², sq.ft, cent, acre and hectare, for the whole field and for every closed sub-division, with a balance line that compares the sub-division total with the overall area.

  • Traditional Ha-a format on the printed sheet, next to the sq.m, cent, acre and sq.ft columns.

  • Equal division of a plot into any number of parts, with each breaking-line length measured and every new intersection point labelled.

  • Angles calculated from the drawing — you enter the three points, the tool prints the degrees.

  • A grouped ladder table — every G-line appears once on the sheet with all of its offset points, even if you entered them in different parts of the table.

A true scale bar that shows the real drawn scale when the drawing is reduced to fit the paper, plus a north arrow and a title block with surveyor, date and sheet number.

Export your FMB drawing in the format you need

    • PDF — for printing, signing and attaching to a file.

    • DXF — opens directly in AutoCAD (R12, one drawing unit = one metre) for further drafting.

    • Excel — details, ladder, connectors, angles, sub-divisions and areas, for your own register.

    • PNG — a picture of the sheet that can go straight into WhatsApp, Word or a report.

    • JSON project file — save the whole project and reopen it later to correct a measurement.

    • Print — the sheet and the full ladder page, fitted to A4, A3 or A2.

FMB Sketch Drawing Online

Looking for Drawing FMB Sketch  Online or an Online FMB Drawing service? Submit your available FMB sketch or relevant land documents for review. Our survey professionals can help you understand the drawing and prepare the required digital representation.

Our FMB Drawing Support Includes

FAQ

Frequently Asked Questions

The SELVI Land Surveyors FMB Sketch Drawer is an online tool designed to create an FMB-style land sketch from survey measurements, G-line points, field lines, offsets, bend points, and subdivision details.

  • Yes. With the SELVI Land Surveyors FMB Sketch Drawer, you can enter the required survey and geometric measurements and generate the drawing on screen. The drawing can be prepared in A3 or A4 paper size with portrait or landscape orientation.
  • The FMB Sketch Drawer uses different line styles to distinguish survey elements. G Lines connect the main reference points such as A, B, C, while Field Lines represent the boundary of the measured field. Subdivision lines can also be added separately.

Yes. The SELVI Land Surveyors FMB Sketch Drawer can calculate the area when the field boundary is properly closed. The calculated area can be displayed in different units, including Hectare, Are, Square Metre, Acre, and Square Feet.

Yes. After completing the drawing, the SELVI Land Surveyors FMB Sketch Drawer provides export options for the project data and drawing, including PDF, DXF, Excel ladder table, and JSON project data, depending on the enabled export options.