Loading an adjacent chunk that "lines up" with the old one.
What don't you understand specifically?
Each chunk is a fixed size (e.g., every chunk is 64x64 tiles, or whatever size you decide to use) and intended to be put in a fixed place in the layout of chunks. That is, the chunk at (1,3) is directly left of the chunk at (2,3). The top row of tiles of one is horizontally adjacent to the top row of tiles on the other.
If you represent tile positions with a single (x,Y) pair then you get the chunk by dividing each component by the chunk width and the position of the tile within that chunk is the remainder of that division. Going again with 64x64 chunks, the tile at world position (127,561) is in chunk (127/64,561/64)=(1,8) and the local position of the tile within that chunk is (127%64,561%64)=(63,49).
Best way to "signal" a new chunk should be loaded.
When the camera distance to the edge of the chunk become smaller than some distance D1, load it. When the distance becomes larger than some other distance D2 (with D2>D1), unload it. Generally you pick D1 such that the load time of a chunk is less than the time it takes for the player to travel the distance D1 so that the chunk is fully loaded up by the time the player can see it. To avoid hitches related to loading, you'll probably want to do at least the I/O portion in a background thread (assuming you don't have easy asynchronous I/O in your language/framework of choice).
General Implementation details
This is an incredible broad one.
One issue to be aware of is that using a naive position for objects like a single global (X,Y) tile position is going to limit the size of your world more than you might like. Such a coordinate can only get so big. There's really no way to realistically give you a truly infinite world, but you do need to apply some thought regarding the size you want to allow. There's a lot of good text online about the size limitations of a 32-bit float, and the size limitations of a 32-bit int are pretty easy to derive.
There are two related approaches to solving this issue (they can even be combined to solve two different aspects of the problem). The first is to split your global position up into two indpendent coordinate pairs (GlobalChunkX,GlobalChunkY) and (LocalTileX,LocalTileY). If the local tile coordinate hits the edge of the chunk size, reset it and increment/decrement the global chunk. e.g.,
if (LocalTileX >= 64)
{
LocalTileX -= 64;
GlobalChunkX += 1;
}
Genericizing the code to handle configurable chunk sizes, both coordinates, and both directions is left as an exercise.
The second approach is to localize coordinates. This is a bit difficult as it affects many parts of your codebase and requires some coordinate between the different parts (though with cleverness you can decouple them somewhat). The idea here is to keep all your global coordinates relative to the center of the camera or some other moving point that remains close to the camera. One option is to make every global coordinate relative to the chunk overlapping the center or upper-left-corner of the camera. In this way the coordinates do not grow without bound; as the player heads left/west, the coordinates constantly reset to be near 0,0 as chunks scroll by. Any coordinates saved to disk can be saved relative to some other position.
One final issue coming to mind is how you know which chunk to load. If you have a big 2D array of all chunks, that array itself is going to be potentially quite large. A better approach is a sparse array. This can be implemented by taking the global position of a chunk (its X,Y coordinate) and using that or a hash of it as a key. The disk layout could be that every chunk saved to disk has the filename X_Y.chunk or some hexadecimal name based on the hash of the (X,Y) coordinate. If you use the hash approach, note that to deal with collisions you'd still want the X_Y part in the filename, e.g. AE6F780D_12_57.chunk. An advantage here is that if you have a lot of chunks you can deal with folder size limitations by breaking up the hash into a folder hierarchy, e.g. the previous becomes AE/6F/78/0D/12_57.chunk.
You'll also want to experiment with chunk size. Too big or too small will cause efficiency and management issues. It's very hard to give advice here without just profiling/measuring your implementation. You should avoid hard-coding a chunk size and make it a value you can change (at compile time is fine). That doesn't mean you need to support worlds with arbitrary chunk sizes at runtime, just make sure you can tweak the value during development until you find something comfortable.