Here comes the solution.
The general idea is, to duplicate the mesh. Then, on the copy, we delete all vertices that are not an apex. Finally, we can turn the copy into points and instantiate objects on the apex.
How does it work
The instantiation should be clear, so I will not go into that. Instead, let’s start with the Delete Geometry node.
As you already wrote, an apex is the highest point of himself and his neighbors. This means, we want to delete a point, if there is any neighbor, that has a higher z-coordinate. And to show a bit of goodwill, we add an Epsilon to it ;-). That is what is happening here: The Delete Geometry node iterates over all vertices of the mesh and the others check for each vertex, if there is a higher neighbor.
The difficult part is, to access the neighbors and compare their heights. This is done by the following set of nodes:
There is no direct way, to compare all neighboring vertices of a certain vertex. But the Edges of Vertex node allows us, to sort neighboring edges by a weight and the Edge Vertices node allows us, to access the positions of the vertices of an edge. Only, it is difficult to access the z-value of the highest vertex of the edge, that we receive from the Edges of Vertex node. To solve this, we calculate this value separately and store it at each edge in the attribute max height (I will explain this later). This allows us to read this value right here.
What do the nodes above do? Evaluate at Index reads the stored max height of the edge, that it receives from Edges of Vertex. Edges of Vertex looks at all edges connected to the current vertex, sorts them descending by the attribute max height and returns the index of the first edge in this order. This is the one, that contains the highest vertex of all these edges.
Now let’s look at the nodes, that calculate the max height attribute for all edges:
Store Named Attribute iterates over all edges of the given geometry. Edge Vertices provides the position of the vertices of every edge. We take the z-coordinate of these positions and store the maximum of them in the attribute max height of the corresponding edge.
What is wrong with your node setup
First let’s take a look at these nodes:
Before, you should take a deep look at this chapter (Fields - Blender Manual) of the blender manual. Fields are a core concept of geometry nodes. You need to understand them thoroughly. Especially, you should understand the difference between circled and diamond-shaped sockets and you should understand the concept of field contexts.
What goes wrong with the above nodes is, that Attribute Statistic does not operate on a field, it operates on a value (circled output socket). This means, it does not give you different minima for every point but one single minimum for all points.
Now let’s take a look at the selection of your Attribute Statistic:
First you store the index of every point on the point itself. Then you use the Attribute Statistic node to iterate over all edges. And you use the attribute, that has been captured on the point in the context of the edge. This gives you the rounded interpolated value of the indices of the vertices of the corresponding edge. Let’s say, you have an edge with vertices with index 1 and 6. Than you will get 4 as the result ((1+6)/2 rounded).
Now you select those edges, where the index of one of its vertices is equal to this interpolated value. This is the case for all edges, that have vertices with subsequent indices (e.g. 1,2 or 5,6).
Finally let’s take a look at which values the Attribute Statistic node gets out of this selection:
If the index of vertex 2 is equal to the interpolated value (which is the same as: if vertex 2 has the higher index), then calculate (z2 – z1) else calculate (z1 – z2).
Overall you take the minimum value of this difference of all edges, that have vertices with subsequent indices and delete all points, if this value is less than -0.1.