;;; ---------------------------------------------------------------------
;;;  SPOTPLOT-bgol.lsp
;;;  Command:  PSLV
;;;
;;;  PURPOSE
;;;  A CAD-native equivalent of the "Plot Spot Levels with Range
;;;  Colour" Excel macro workflow: reads a CSV of survey points
;;;  (X,Y,Z) directly inside the drawing, automatically buckets each
;;;  point's elevation into one of several colour bands, plots the
;;;  elevation as TEXT at its coordinate in the matching AutoCAD
;;;  colour, and draws a simple colour-coded legend so terrain highs
;;;  and lows are visible at a glance -- without ever leaving CAD or
;;;  round-tripping through Excel.
;;;
;;;  USAGE
;;;    1. Load this file (APPLOAD or drag-drop onto the drawing window).
;;;    2. Type   PSLV   at the command line and press Enter.
;;;    3. Select the source CSV file (rows: X,Y,Z).
;;;    4. Enter the number of colour bands (or accept the default, 5).
;;;    5. Enter the text height for the plotted elevation labels.
;;;    6. Pick the legend insertion point.
;;;    7. Elevation text is plotted at each point's X,Y coordinate in
;;;       a colour matching its elevation band, all on layer
;;;       "BGOL-PSLV"; a colour-coded legend is drawn at the picked
;;;       point.
;;;
;;;  NOTES
;;;    - Colour bands are computed automatically: evenly spaced
;;;      between the data's minimum and maximum elevation, using
;;;      AutoCAD colour indices 1 (red) through 9 for up to 9 bands.
;;;    - This is an original, independently written implementation.
;;;      It is INSPIRED by the general idea of color-coded spot-level
;;;      plotting utilities common in survey/earthwork workflows
;;;      (typically distributed as spreadsheet macros), but the code,
;;;      command name and file name here are new, entirely
;;;      AutoLISP-native, and are NOT copied from, nor identical to,
;;;      any specific third-party product.
;;;
;;;  ---------------------------------------------------------------------
;;;  Developed and shared for the CAD community by:  BGol Community
;;;  Community website:                              https://bgol.in/
;;;  License:  Open-source & free to use, modify, and share for all.
;;;            Provided "as is", without warranty of any kind.
;;;  ---------------------------------------------------------------------

(vl-load-com)

(defun BGOL:PSLV-EnsureLayer (lname / )
  (if (not (tblsearch "LAYER" lname))
    (entmake (list '(0 . "LAYER")
                   '(100 . "AcDbSymbolTableRecord")
                   '(100 . "AcDbLayerTableRecord")
                   (cons 2 lname)
                   '(70 . 0)
                   '(62 . 7)
                   '(6 . "Continuous")))
  )
)

(defun BGOL:PSLV-Split (str delim / pos out remain)
  (setq out '())
  (setq remain str)
  (while (setq pos (vl-string-search delim remain))
    (setq out (append out (list (substr remain 1 pos))))
    (setq remain (substr remain (+ pos 1 (strlen delim))))
  )
  (setq out (append out (list remain)))
  out
)

(defun BGOL:PSLV-MakeText (pt txt height clr lname / dxfdata)
  (setq dxfdata (list '(0 . "TEXT")
                       '(100 . "AcDbEntity")
                       (cons 8 lname)
                       (cons 62 clr)
                       '(100 . "AcDbText")
                       (cons 10 pt)
                       (cons 40 height)
                       (cons 1 txt)
                 )
  )
  (entmake dxfdata)
)

;; Determine colour index (1-9) for value v given min/max/nbands.
(defun BGOL:PSLV-BandColor (v vmin vmax nbands / span idx)
  (setq span (- vmax vmin))
  (if (<= span 0.0)
    1
    (progn
      (setq idx (fix (* nbands (/ (- v vmin) span))))
      (if (>= idx nbands) (setq idx (1- nbands)))
      (1+ (rem idx 9))
    )
  )
)

(defun C:PSLV ( / path f line fields x y z pts height nbands
                  vmin vmax legpt i clr band bandmin bandmax
                  legy rowh sw)
  (BGOL:PSLV-EnsureLayer "BGOL-PSLV")
  (setq path (getfiled "Select Spot Level Data File (X,Y,Z)" "" "csv;txt" 0))
  (if path
    (progn
      (setq nbands (getint "\nNumber of colour bands <5>: "))
      (if (not nbands) (setq nbands 5))
      (setq height (getreal "\nText height <2.5>: "))
      (if (not height) (setq height 2.5))
      (setq f (open path "r"))
      (setq pts '())
      (if f
        (progn
          (while (setq line (read-line f))
            (if (and line (> (strlen (vl-string-trim " \t" line)) 0))
              (progn
                (setq fields (BGOL:PSLV-Split line ","))
                (if (>= (length fields) 3)
                  (progn
                    (setq x (atof (nth 0 fields)))
                    (setq y (atof (nth 1 fields)))
                    (setq z (atof (nth 2 fields)))
                    (setq pts (append pts (list (list x y z))))
                  )
                )
              )
            )
          )
          (close f)
          (if (> (length pts) 0)
            (progn
              (setq vmin (caddr (car pts)))
              (setq vmax vmin)
              (foreach p pts
                (setq z (caddr p))
                (if (< z vmin) (setq vmin z))
                (if (> z vmax) (setq vmax z))
              )
              (foreach p pts
                (setq clr (BGOL:PSLV-BandColor (caddr p) vmin vmax nbands))
                (BGOL:PSLV-MakeText (list (car p) (cadr p) (caddr p))
                                    (rtos (caddr p) 2 3) height clr "BGOL-PSLV")
              )
              (setq legpt (getpoint "\nPick legend insertion point: "))
              (if legpt
                (progn
                  (setq rowh (* height 1.8))
                  (setq sw (* height 1.2))
                  (setq legy (cadr legpt))
                  (setq i 0)
                  (while (< i nbands)
                    (setq bandmin (+ vmin (* (- vmax vmin) (/ (float i) nbands))))
                    (setq bandmax (+ vmin (* (- vmax vmin) (/ (float (1+ i)) nbands))))
                    (setq clr (1+ (rem i 9)))
                    (entmake (list '(0 . "LWPOLYLINE")
                                   '(100 . "AcDbEntity")
                                   (cons 8 "BGOL-PSLV")
                                   (cons 62 clr)
                                   '(100 . "AcDbPolyline")
                                   '(90 . 4) '(70 . 1)
                                   (cons 10 (list (car legpt) legy))
                                   (cons 10 (list (+ (car legpt) sw) legy))
                                   (cons 10 (list (+ (car legpt) sw) (+ legy rowh)))
                                   (cons 10 (list (car legpt) (+ legy rowh)))
                             )
                    )
                    (BGOL:PSLV-MakeText (list (+ (car legpt) sw (* height 0.6)) (+ legy (* rowh 0.25)) 0.0)
                                        (strcat (rtos bandmin 2 2) " - " (rtos bandmax 2 2))
                                        (* height 0.85) 7 "BGOL-PSLV")
                    (setq legy (- legy rowh))
                    (setq i (1+ i))
                  )
                )
              )
              (princ (strcat "\nPSLV: " (itoa (length pts)) " spot level(s) plotted in " (itoa nbands) " colour band(s) on layer BGOL-PSLV."))
            )
            (princ "\nNo valid X,Y,Z rows found in the file.")
          )
        )
        (princ "\nCould not open the selected file.")
      )
    )
    (princ "\nNo file selected.")
  )
  (princ)
)

(princ "\nSPOTPLOT-bgol.lsp loaded. Type PSLV to plot colour-banded spot levels from a CSV file.")
(princ "\n  -- Free & open-source, courtesy of BGol Community (https://bgol.in/) --")
(princ)
