[Question]
  [
Given a rectangular matrix of elevations, draw its contours.
# Task
Two elements `x` and `y` are on the same contour level if `floor(x/10) == floor(y/10)`. For example, `52` and `58` are on the same contour level, but `58` and `64` are not.
The act of drawing contours is defined as follows:
For every element `e`, replace it with a two-char string chosen as follows:
* the first character is `" "` if the element below `e` is on the same contour level as `e` or if there is no element to the below `e` and `"_"` otherwise
* the second character is `" "` if the element to the right of `e` is on the same contour level as `e` or there is no element to the right of `e` and `"|"` otherwise
The elements within rows are joined together, then the rows are joined together with newlines.
# Example
Let's say the input is `[[5,20],[3,6]]`, visualized as 
```
5 20
3 6
```
We first look at `5`. Since `3` is on the same contour level as `5`, the first character is `" "`. Since `20` is not on the same contour level as `5`, the second character is `"|"`.
Now we look at `20`. Since `6` is not on the same contour level as `20`, the first character is `"_"`. Since there is no element to the right of `20`, the second character is `" "`.
Now we look at `3`. Since there is no element below `3`, the first character is `" "`. Since `6` is on the same contour level as `3`, the second character is `" "`.
Now we look at `6`. Since there is no element below `6`, the first character is `" "`. Since there is no element to the right of `6`, the second character is `" "`.
Based on these two-char strings, we make replacements to get `[[" |","_ "],[" "," "]]`. Joining these together, we get an output of 
```
 |_ 
    
```
# Rules
* The input matrix will always be rectangular and composed of positive integers.
* Trailing spaces or newlines may be in any amount (including 0) and do not have to be consistent in any way.
* You do not have to follow the same algorithm as long as you produce the same results.
* Your program or function may output a newline-separated string, list of strings, or equivalent.
* This is [code-golf](/questions/tagged/code-golf "show questions tagged 'code-golf'"), so shortest code in bytes wins.
# Test Cases
```
input
output
[[1,5,8,9],[3,11,13,8],[7,14,10,9],[4,8,7,6]]
  _ _  
 |   | 
 |_ _| 
[[0,10,20,30,40,50,60,70,80,90],[0,0,10,10,20,20,30,30,40,40],[0,0,0,10,10,10,20,20,20,30],[0,0,0,0,10,10,10,10,20,20],[0,0,0,0,0,10,10,10,10,10],[0,0,0,0,0,0,10,10,10,10],[0,0,0,0,0,0,0,10,10,10],[0,0,0,0,0,0,0,0,10,10],[0,0,0,0,0,0,0,0,0,10],[0,0,0,0,0,0,0,0,0,0]]
 |_|_|_|_|_|_|_|_|_
   |_  |_ _|_ _|_ _
     |_    |_ _  |_
       |_      |_ _
         |_        
           |_      
             |_    
               |_  
                 |_
[[5,5,5,5,5,5,5,5,5,5,5],[5,10,10,10,10,10,10,10,10,10,5],[5,10,15,15,15,15,15,15,15,10,5],[5,10,15,20,20,20,20,20,15,10,5],[5,10,15,20,25,25,25,20,15,10,5],[5,10,15,20,25,30,25,20,15,10,5],[5,10,15,20,25,25,25,20,15,10,5],[5,10,15,20,20,20,20,20,15,10,5],[5,10,15,15,15,15,15,15,15,10,5],[5,10,10,10,10,10,10,10,10,10,5],[5,5,5,5,5,5,5,5,5,5,5]]
  _ _ _ _ _ _ _ _ _  
 |                 | 
 |    _ _ _ _ _    | 
 |   |         |   | 
 |   |    _    |   | 
 |   |   |_|   |   | 
 |   |         |   | 
 |   |_ _ _ _ _|   | 
 |                 | 
 |_ _ _ _ _ _ _ _ _| 
[[35,32,29,26,25,25,25,26,29,32,35],[32,28,25,22,20,20,20,22,25,28,32],[29,25,21,18,15,15,15,18,21,25,29],[26,22,18,14,11,10,11,14,18,22,26],[25,20,15,11,7,5,7,11,15,20,25],[25,20,15,10,5,0,5,10,15,20,25],[25,20,15,11,7,5,7,11,15,20,25],[26,22,18,14,11,10,11,14,18,22,26],[29,25,21,18,15,15,15,18,21,25,29],[32,28,25,22,20,20,20,22,25,28,32],[35,32,29,26,25,25,25,26,29,32,35]]
  _|             |_  
_|    _ _ _ _ _    |_
    _|         |_    
   |    _ _ _    |   
   |   |     |   |   
   |   |     |   |   
   |   |_ _ _|   |   
   |_           _|   
_    |_ _ _ _ _|    _
 |_               _| 
   |             |  
```
  
[Answer]
# [Perl 6](https://perl6.org), 135 bytes (131 chars)
```
{my$n="_";sub w{$^a.chop-$^b.chop??$n!!" "};my&q={|.[1..*],.tail};(($_ «[&w]».&q) ZZ~{$n="|";$_ «[&w]».map(*.&q)}()).map:{say |$_}}
```
[Try it online!](https://tio.run/##jVNRboJAEP33FEg3ZtdMye7CIpYY76FRg01Nm4jV2sYQpBfqEfrnxejMNlZsUAwDmX3vzdvZAdZPb8uwbKWZwxbOoMzTjK0G7syNtx9zZ5ezaeI9Pr@u79l0bpPhkK3abddxizjNOptBvvfGyvO6E/Dek5dlEXPOZs7ha9zZTQ7fXmcjnNHoMyfTvRtXqTRZ8y4JCi4ErR7ybZI5ezYrijJuUc4WnHMFBiLoC@A@KAXKhwjzHqgAlLR4gHwPQiFEpcw34GvQfdAhaHOMkBDEfUN@yEcWxkQeQ1skQhVKqB5XuG0EyhwjIoRw2p08teUD25@0z8Cq0CskiSFnqlTYqMGbJBbU5oyXyNKtZD1/of6GFpoPcsM4God69goM1Fzo8nu8C3ESmLo4F5zalJUJ/hP89XlZ4MsGQYPD1R4aTnF1DnUDpBnfVYaspf037Pdf/gA "Perl 6 – Try It Online")
Slightly ungolfed:
```
{
    my $n = "_";
    sub w { $^a.chop - $^b.chop ?? $n !! " "};
    my &q = {|.[1..*],.tail};
    (
        ($_ «[&w]».&q)
        ZZ~
        {$n="|";$_ «[&w]».map(*.&q)}()
    ).map:{say |$_}
}
```
**Explanation**: First, we define a variable `$n` (line 2) and a function `w` (line 3). This function returns a space if its two arguments are at the same "elevation", and the contents of variable `$n` otherwise. Instead of dividing by 10 and flooring, we abuse the fact that ints are `Cool` (can be treated like strings) and use `chop` to remove the last character (=digit). Then we calmly subtract them, forcing them into numbers again :—).
After that (line 4) we make up a function `q` that takes a list and returns that list with first element removed and the last element duplicated.
On the next 3 lines, we're going to make 2 more matrices from the input matrix: the first one has the first row missing and the last row duplicated (that's just `.&q` — using `.&`, you can call a function on anything as if it was a method — the thing in front of the dot is then the first argument), the other one has the first column missing and the last column duplicated (that's `.map(*.&q)`).
First (line 4) we take the original matrix `$_`, "overlay" it with the "shifted rows" matrix and use the function `w` as a binary operator (that's that `[&w]`) on the matching elements. That one puts a `_` wherever the matching elements are on the different elevations, and a  otherwise. So we get ½ of the result (only the "first characters").
At line 6, we do the same thing, but first we change `$n` to `|`, and we now "overlay" the original matrix with the matrix with shifted columns. Result has a `|` at different and  at the same elevations. These are the "second characters".
Now we just combine them together. We zip the arrays with a zip with a concat (yeah...), which results in a matrix of the original shape whose each element is the 2 matching elements of the "half-solutions" concatenated. Finally, we just map over this matrix (which is really a list of lists). Each of these lists is flattened and then `say`-ed (printed with a newline). Since `say` can take any number of arguments and it prints them all without separators, making the newline only at the end, we get the desired picture on stdout. (And the block returns a list of `True`s (each `say` returns one `True`), but who cares.)
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), ~~25 23~~ 22 [bytes](https://github.com/DennisMitchell/jelly/wiki/Code-page)
-1 byte thanks to [miles](https://codegolf.stackexchange.com/users/6710/miles) (`I` vectorises)
```
:⁵I;€0ao⁶
Zç”_Zż"ç”|$Y
```
A full program printing the result. As a monadic link it takes a list of lists of numbers, the elevations, and returns a list lists, however these "rows" consist of lists of two character "pairs" - if this is fine then 1 byte may be saved by removing `Y`.
**[Try it online!](https://tio.run/##y0rNyan8/9/qUeNWT@tHTWsMEvMfNW7jijq8/FHD3Pioo3uUwKwalcj///9HRysY6Bga6BgZ6Bgb6JgY6Jga6JgZ6Jgb6FgY6FgaxOpwKYBUQBRB1EGUQlSbIKuAK4KrAytFVYGsCKYOQwWaIkOsKhSIUaFAjAoFYlQoEKNCwSA2FgA "Jelly – Try It Online")**
### How?
```
:⁵I;€0ao⁶ - Link 1, assignCharacters (row-wise): list of lists of numbers; character, c
 ⁵        - literal 10
:         - integer division (vectorises)
  I       - incremental differences (vectorises) (zero if the same else non-zero)
     0    - literal 0
   ;€     - concatenate for €ach (rightmost edge of a row has no contour mark)
      a   - logical and (vectorises) with c (replace non-zeros with the contour character)
        ⁶ - literal space character
       o  - logical or (vectorises) (replace the zeros with spaces)
Zç”_Zż"ç”|$Y - Main link: list of lists of numbers, contours
Z            - transpose the input (get the columns)
  ”_         - literal underscore character, '_'
 ç           - call the last link (1) as a dyad with '_'
    Z        - transpose the result
          $  - last two links as a monad:
        ”|   -   literal pipe character, '|'
       ç     -   call the last link (1) as a dyad with '|'
      "      - zip with the dyadic operation:
     ż       -   zip (interleave the column-wise characters with the row-wise ones)
           Y - join with newlines
             - implicit print
```
[Answer]
# [Python 2](https://docs.python.org/2/), ~~199~~ ~~186~~ ~~157~~ 155 bytes
```
lambda a:(lambda x:'\n'.join(''.join('_ '[x==z]+'| '[x==y]for x,y,z in zip(r,r[1:]+r[-1:],q))for r,q in zip(x,x[1:]+x[-1:])))([[v/10for v in r]for r in a])
```
[Try it online!](https://tio.run/##jZPdboJAEIXvfYq9g43Tdnf5EUm89SWQNDSWlKZF3RiDpu9OZxaXQoNAXGA859vJ7Akcr@ePQ6nqfLOrv7Lvt33Gsti9V1Xs7Ern@fNQlK5jn6/MSarN5pYunZ@mvKb5QbMKrnBjRcluxdHVoBMZp0udPOEDTpwTouFkgQoqA1QG4Jy7SXJ5kYKwC0HaNNVUZimv9@8527oZjxfsqIvyzDJb5Kje68Vii20kBBDBOoXEAylBehBhvQLpgxRG99FfQZimvNkhyFACPAG@gEBAKGAlIEJaIC7AAA3TYA3pW9f6LWKo1uzYluh4fVf2rTFnxHisP5RFG0cAAz/cE/wbtLf@gGBo9QElemsYCOx6DHhiApjoMDrDxClGcxgK0Mbr4dwK1BpU2BkxJAV1jxqQHxlZdYZURomQQoT24z98v6POgBEppNNrTj2V8X3zIQhz9w2FvUJC2nAkfhEBXoTc0@v5eDKgq5PunP0zRpg@yIw4JkPF9Otf "Python 2 – Try It Online")
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), 24 bytes
```
:⁵IṠ;€0
ZÇZị⁾_ +³Ç¤ị⁾| ¤
```
[Try it online!](https://tio.run/##y0rNyan8/9/qUeNWz4c7F1g/alpjwBV1uD3q4e7uR4374hW0D20@3H5oCYRbo3Boyf/D7ZH//0dHG@qY6ljoWMbqRBvrGBrqGBrrWADZ5jqGJjqGBmBxE6C8uY5ZbCwA "Jelly – Try It Online")
# Explanation
```
:⁵IṠ;€0           Helper Link; get contour data
:                 Floor division by
 ⁵                10
  I               Compute increments
   Ṡ              Sign; ±1 for different values and 0 for same values
    ;             Append
      0           Zero
     €            To each row
ZÇZị⁾_ +³Ç¤ị⁾| ¤  Main Link
Z                 Zip the input (for vertical contours _)
 Ç                Get the contour data
  Z               Zip the data (because it's zipped from the first Z)
   ị              Index into the string
    ⁾_            "_ "
       +          Add (vectorizing twice) to
        ³ ¤    ¤  Nilad starting from (input)
         Ç        Get contour data (horizontal contours |)
           ị      Index into the string
            ⁾|    "| "
```
-2 bytes thanks to Jonathan Allan
[Answer]
# [Python 2](https://docs.python.org/2/), 226 bytes
```
l=[[j/10for j in i]for i in input()]
for i,j in enumerate(l[:-1]):print''.join('_ '[h==l[i+1][g]]+'| '[h==j[g+1]]for g,h in enumerate(j[:-1]))+'_ '[j[-1]==l[i+1][-1]]
print''.join(' '+'| '[i==j]for i,j in zip(l[-1],l[-1][1:]))
```
[Try it online!](https://tio.run/##jVNNj5swEL3zK6xcSJTp1jYfYSPluL320ptlVavUTYyygLxE3a3639OZYUOhSkLAIPu9N4/xk2ne231d6dO2/uHERoTZbHY6bIwpPyv5sw6iFL4S3tLU87Rqju18YSNGgGlXHV9ceG7d/GDWn5RdrJvgqzaOH8raV/P4u4jNfrM5GL9U1uysXcZ/Oqg0O4TYfQf7sVfZeS2WXF8aXPQeOLfR@CMi7lw9utpBc799g21hAfDbqDV6nnCbUdSG93Uk8Pq19wcnvoWj69Z0uTe3FZRKj/D3Ive2dU0rnr5@eQqhDl1B8/z6ejImAy0tmARybM8YBRkU8MiIUqASKHC@ApWCkoynyK8@1JJALSGRkErIJOQSVhIKVJKpBBZ0mk7WKdMze@Z7Cat6ckCfFQNuzKoxdYu5QVzHr8KSo8jgwo367L8mR@OfILs0xgItR@OyIDuP64JETggmHG72MLGLmzlcCpCiTbBnDfoRdD5oLycE8YSKiS8Y1oMGNSMFqlBC9bjCM10MmisIIZyONnlq5lM@/JLfKavQKydJH4zCvyDDhyQfyY143BXQM0j2nvo7WpjeyB1xTIZq/wI "Python 2 – Try It Online")
Whew, that was a doozy to work the logic out. I now see Hyper Neutrino ninja'd me with a shorter answer but I spent too much work on this *not* to post it. :P
Also, can I just say, this is an awesome way to make ASCII-art. Excuse me while I make a boatload more of these.
[Answer]
# J, 58 bytes
```
f=.{~0==/@]
[:(,/"2)2 2((' _'f{."1),' |'f{.);.3 1:+<.@%&10
```
[Try it online!](https://tio.run/##dZDRCoJAEEXf9ysuYak0bDPramUt9B8RPUQagfYBRr9uW7IRlMzLvefcp7n2feV092DnFruDqp3elwktJiY1MEkS4xhXnZ5ISjHur5hiozNIOd/q3XQmrBqnLSwiCHKssIa3Asl8XkIshD2z71ao1mnfhSMwCZNhypgsU85UMC2ZVkxrptdJWAyjYWcHF@xnMGx@5Mf/d/ItxvkoHqMjkNX5dLmh9n8yDCr9p4qAmhDavn8C)
An anonymous function which takes a matrix and outputs the contours.
Lotta room for improvement here. I didn't have time to try all the test cases so let me know if there are any issues. Will try to golf more and explain later.
# (Quick) Explanation
Helper function: indexes into a length 2 string based on whether the first element of a 2 length array is equal to the second. If it is equal, it indexes into the zeroth element, if it is unequal, it indexes into the first. A 1 length array always indexes into the zeroth element of the string.
```
f=.{~0==/@]
```
Main function
```
[:(,/"2)2 2((' _'f{."1),' |'f{.);.3 1:+<.@%&10
```
`1:+<.@%&10` floors each element divided by 10 and adds 1 (so we will never get 0 -- this is important for the helper function).
`2 2((' _'f{."1),' |'f{.);.3` cuts the matrix into 2 x 2 segments if it can (otherwise it'll give a 2 x 1, 1 x 2, or 1 x 1 segment near the edges) and applies the function that uses `f` to compare the top left element to the top right and the top left element to the bottom left.
`(,/"2)` flattens the result into the desired shape. I really feel like I should be able to avoid having to use this (and a lot of other things, but I digress).
[Answer]
# [J](http://jsoftware.com/), ~~46~~ 45 bytes
```
' _|'({~];"1@(,&.>-@|:)&(0,~}.~:}:)|:)<.@%&10
```
[Try it online!](https://tio.run/##hZNNboMwEIX3nOIpUvmRKJoxhhDSVBykURZVUdVNF1k2cHU6tksKigExIPt9M/b4yXwN7RWnGgR5hwiXWxT/9Ofjjps4DbPX5@ZWJ2FMad9lfd3ViUxfsuYpZBqSYJchak91hBRdjfYaBB/vn9@Ij22Ci8L5DQUUIUc5BdoAFlThIIwZnMt4D9ZgEk3b2axGdCkiw82CBE0oCCVhT6ikiEAOuwyX5PK0YyO9J7icB3jnfsZTsKwvykvqgkgzF9h56n@YFmPEhS@mWNEsfLgYYwnntIpXq1f2Xu185dye59HTXNpWUAeoctJjaRTR/2hlRTXpUlmlGmtlLJe5mnRYGcXobmVlqbZ3nuxX2xw17utOynL9C/ND8Ny2f1PIY@pK7ebWW81vHn/LwOEX)
[Answer]
## JavaScript (ES6), ~~120~~ 118 bytes
```
a=>a.map(b=>b.map(c=>c/10|0)).map((b,i,a)=>b.map((c,j)=>((a[i+1]||0)[j]-c?'_':' ')+(b[j+1]-c?'|':' ')).join``).join`\n`
```
Where `\n` represents the literal newline character. Edit: Saved 2 bytes thanks to @Bálint.
```
f=
a=>a.map(b=>b.map(c=>c/10|0)).map((b,i,a)=>b.map((c,j)=>((a[i+1]||0)[j]-c?'_':' ')+(b[j+1]-c?'|':' ')).join``).join`
`
;[
[[5,20],[3,6]]
,
[[1,5,8,9],[3,11,13,8],[7,14,10,9],[4,8,7,6]]
,
[[0,10,20,30,40,50,60,70,80,90],[0,0,10,10,20,20,30,30,40,40],[0,0,0,10,10,10,20,20,20,30],[0,0,0,0,10,10,10,10,20,20],[0,0,0,0,0,10,10,10,10,10],[0,0,0,0,0,0,10,10,10,10],[0,0,0,0,0,0,0,10,10,10],[0,0,0,0,0,0,0,0,10,10],[0,0,0,0,0,0,0,0,0,10],[0,0,0,0,0,0,0,0,0,0]]
,
[[5,5,5,5,5,5,5,5,5,5,5],[5,10,10,10,10,10,10,10,10,10,5],[5,10,15,15,15,15,15,15,15,10,5],[5,10,15,20,20,20,20,20,15,10,5],[5,10,15,20,25,25,25,20,15,10,5],[5,10,15,20,25,30,25,20,15,10,5],[5,10,15,20,25,25,25,20,15,10,5],[5,10,15,20,20,20,20,20,15,10,5],[5,10,15,15,15,15,15,15,15,10,5],[5,10,10,10,10,10,10,10,10,10,5],[5,5,5,5,5,5,5,5,5,5,5]]
,
[[35,32,29,26,25,25,25,26,29,32,35],[32,28,25,22,20,20,20,22,25,28,32],[29,25,21,18,15,15,15,18,21,25,29],[26,22,18,14,11,10,11,14,18,22,26],[25,20,15,11,7,5,7,11,15,20,25],[25,20,15,10,5,0,5,10,15,20,25],[25,20,15,11,7,5,7,11,15,20,25],[26,22,18,14,11,10,11,14,18,22,26],[29,25,21,18,15,15,15,18,21,25,29],[32,28,25,22,20,20,20,22,25,28,32],[35,32,29,26,25,25,25,26,29,32,35]]
].forEach(a=>document.write(['<pre>','</pre>'].join(f(a))));
```
[Answer]
# [Proton](https://github.com/alexander-liao/proton), 202 bytes
```
R=(L=len)+range
k=[map((//)&10,r)for r:eval(input())]
d=(x,y,X,Y)=>X>=L(k)or Y>=L(k[X])or k[x][y]==k[X][Y]
print('\n'.join(map(''.join,[['_ '[d(x,y,x+1,y)]+'| '[d(x,y,x,y+1)]for y:R(k[x])]for x:R(k)])))
```
[Try it online!](https://tio.run/##jZBva4MwEMbf@yn6asnhsRptnC2kn6Cv@krJwhBqh7OLIm4o7Lu7XIqMwUYLl3D3e567/On6dmjtPB8VP6hLZSHsS/taBY3S72XH@XoNDyLCHs5tv@p31Wd54bXtPgYOYIKT4iNOmGMBap/v1YE34HyFz3RuqGj0aPRklCKgCxN0fW0Hzp4te3xra8vpHHbNUWv2smL65MeOocAJTMi@fhBOoQBDl5l2R06zr9VIFRgAmGetE4lJjPEW4xRjuURKxPFEGtSkZx67JFoi9iRzLmehflcJFBkKuURGhPiWLCn1kL5B4YyR3zfe5WalZJE0mToFPqF0iywexvKXHjmVloj@1v/pv@MKtx9yx3fc/FTzDQ "Proton – Try It Online")
-2 bytes thanks to Jonathan Frech  
-15 bytes by switching to Proton instead of Python 2
[Answer]
# Java 8, ~~200~~ ~~170~~ 169 bytes
```
a->{String r="";for(int l=a.length,i=0,j;i<l;i++,r+="\n")for(j=0;j<l;r+=(i>l-2||a[i][j]/10==a[i+1][j]/10?" ":"_")+(j++>l-2||a[i][j-1]/10==a[i][j]/10?" ":"|"));return r;}
```
**Explanation:**
[Try it here.](https://tio.run/##rZTfk5owEMff@1dkeIJhtQm/5Eq5/gW9lz5a5yb1uGsoFx3E63SUv91uAlLsgBSqbhjZ/X7c7GYnKX/js802kenTj9M647sd@cyFPLwjRMgiyZ/5OiEP6pWQL0Uu5AtZmxhZrpYrwq0IAyUutF3BC7EmD0SSmJz47P5Q6/PYMKLnTa4wksV8niXypfgOIqaQRuJjFgnbhtyOja/SsJQwjWmUoh99prjPZs7xyJditUxX7xmNY/xts/rtk0GMD8ajYdlmattt7Yw16gvt0bCsKE@KfS5JHpWnSNemS1Cf7f5bhmXU1bxtxBN5xYaYVTG66EMtxY782hXJ63yzL@ZbDBemnK9NmfwkdYf@KAlp3AcfHFpCZ8iFoGwCpWVF/5mKgQ8h3PVmYwyYC2FPfAHMA0Z7eQ//e3HjHVOV0KHgUvAo@BQCCgsKIe6ir2kUNFRxFVrR3jXizDSYJq8CLeRMDegvCTYsH6seKR6nHSWlNx0DHzq@PXvx/2ryhV2H/C4bhhx6Yf8O@WcbB7l0AjQh0@iaJnRv9Dl1DcItR83F/jrg3IETtNoWKA/63b6NKSbUUqfVOEd7QiR7MJUHFXjvhq2mhcqj/H1XrdqPoxlPX9pUPz1NYs6gD2sGgOFN7eNSWD0hgwyeCqjVmqpb5plY0rQGTjyuweHomsTKWZ5@Aw)
Note that integer division in Java automatically floors.
```
a->{                   // Method with 2D int-array as parameter and String return-type
  String r="";         //  Result-String
  for(int l=a.length,  //  Length of the input array
      i=0,j;           //  Index integers
      i<l;i++,         //  Loop (1) over the rows of the input array
          r+="\n")     //  and append a new-line to the result after every iteration
    for(j=0;j<l;       //   Inner loop (2) over the columns of a row
      r+=              //    Append the String with:
         (i>l-2        //      If it's the last row,
         ||a[i][j]/10==a[i+1][j]/10?
                       //      or the current and next rows are equal floored/10:
          " "          //       Use a space
         :             //      Else:
          "_")         //       Use a "_"
        +              //     Plus
         (j++>l-2      //      If it's the last column in the row,
         ||a[i][j-1]/10==a[i][j]/10?
                       //      or the current and next columns are equal floored/10:
          " "          //       Use a space
         :             //      Else:
          "|")         //       Use "|"
    );                 //   End of column loop (2)
                       //  End of row-loop (1) (implicit / single-line body)
  return r;            //  Return the result-String
}                      // End of method
```
[Answer]
# R, 159 bytes
```
f=function(m){M=m%/%10;a=cbind(0,t(apply(M,1,diff)));b=rbind(apply(M,2,diff),0);a[!!a]="|";b[!!b]="_";M[]=gsub("0"," ",paste0(a,b));write(t(M),"",ncol(m),,"")}
```
With newlines and indentations:
```
f=function(m){
    M=m%/%10
    a=cbind(0,t(apply(M,1,diff))) #row-wise difference
    b=rbind(apply(M,2,diff),0) #column-wise difference
    a[!!a]="|"
    b[!!b]="_"
    M[]=gsub("0"," ",paste0(a,b)) # M[] is a trick to force the result to have the same structure as M
    write(t(M),"",ncol(m),,"")
    }
```
Does the integer division of the matrix, measures the row-wise and column-wise differences, and when not null replace by `|`and `_` respectively, then pastes both (painless, thanks to R's vectorization) and outputs.
Test cases:
```
> m=matrix(c(0,10,20,30,40,50,60,70,80,90,0,0,10,10,20,20,30,30,40,40,0,0,0,10,10,10,20,20,20,30,0,0,0,0,10,10,10,10,20,20,0,0,0,0,0,10,10,10,10,10,0,0,0,0,0,0,10,10,10,10,0,0,0,0,0,0,0,10,10,10,0,0,0,0,0,0,0,0,10,10,0,0,0,0,0,0,0,0,0,10,0,0,0,0,0,0,0,0,0,0),byrow=T,ncol=10)
> f(m)
  |_|_|_|_|_|_|_|_|_
    |_  |_ _|_ _|_ _
      |_    |_ _  |_
        |_      |_ _
          |_        
            |_      
              |_    
                |_  
                  |_
                    
> m=matrix(c(5,5,5,5,5,5,5,5,5,5,5,5,10,10,10,10,10,10,10,10,10,5,5,10,15,15,15,15,15,15,15,10,5,5,10,15,20,20,20,20,20,15,10,5,5,10,15,20,25,25,25,20,15,10,5,5,10,15,20,25,30,25,20,15,10,5,5,10,15,20,25,25,25,20,15,10,5,5,10,15,20,20,20,20,20,15,10,5,5,10,15,15,15,15,15,15,15,10,5,5,10,10,10,10,10,10,10,10,10,5,5,5,5,5,5,5,5,5,5,5,5),byrow=T,ncol=11)
> f(m)
   _ _ _ _ _ _ _ _ _  
  |                 | 
  |    _ _ _ _ _    | 
  |   |         |   | 
  |   |    _    |   | 
  |   |   |_|   |   | 
  |   |         |   | 
  |   |_ _ _ _ _|   | 
  |                 | 
  |_ _ _ _ _ _ _ _ _| 
                      
```
[Answer]
# [Perl 5](https://www.perl.org/), ~~130~~ 126 bytes
124 bytes of code + 2 for `-ap` flags
```
push@a,[map 0|$_/10,@F]}{map{say map{($a[$r+1][$c]-$_&&$r<$#a?'_':$").($a[$r][++$c]-$_&&$c<@{$a[0]}?'|':$")}@$_;$c=0;$r++}@a
```
[Try it online!](https://tio.run/##fY/LCsIwEEX3fkXQoVX6cKJkYyvNyp1fEEIJRVBQG1pdSNtfN1alohDDXBiYc@eld9WRGaOv9Z6rUJyUJthCPqcY8o3smr7Q1OpGnnkKSkAVUCmgkBHkngdVChOV@bm/gvEsfhukCIKPoUh501dRdpnfvlwdhzyBYo1JPyvouDKGEUuMGKH4VwNmNn3jBf7Ihtmgf3iJTuzsdux2Xu742xL3Ul8O5bk20ZbFSNFESj8A "Perl 5 – Try It Online")
Input format is 2-D grid of space separated numbers.
**Explanation**
*This is from a previous iteration of the code.*
```
push@a,[map 0|$_/10,@F]     # read the input, divide it by 10, and store it in a 2-D array
}{                          # end the implicit while loop and start the final block
map{                        # repeat this for each line
  $_=($a[$r+1][$c]-$_&&$r<$#a?'_':$")       # set appropriate characters to output based
     .($a[$r][++$c]-$_&&$c<@{$a[0]}?'|':$") # on the given rules
  for@$_;                                   # repeat for each number on the line
  $c=0;$r++;                         # setup row and column counters for next iteration
  say@$_                             # output this line
}@a
```
]