Unfortunately you've found the limitations of flat scene graphs. The CPU has to (for each osg::Viewer frame() call) iterate through all objects in the scene graph. If you don't break up the objects into parenting groups that may be culled (not rendered) because none of the children 's bounding boxes are within the osg::Viewer's camera. I'm assuming you've parented the osgEarth::ModelNodes to the root osgEarth::MapNode. By doing this, the frame() call actually traverses the graph for every single node. I bet if you made groups based on location (like moving things based on where they are latitudinally / longitudinally) and you zoom in a bit more, then you should see your FPS recover a bit. Zooming out will always be of this poor performance though since scene graph's, like osg, weren't meant for this type of behavior in the first place. Hopefully this information helps you a bit.
Blanky is right. Depending on how complex your boat model is you may need to implement an LOD scheme. (This is more of an OSG topic, you could check the OSG mailing list for more info.) Also, ModelNode is kind of a "heavyweight" class that is more suited to earth file annotations -- you might fare better just using a simple GeoTransform above your model. Hope this helps.
The obvious way to approach this would be OpenGL instancing, if you just need to display the same model 1000 times. You'd probably would not even need an LOD scheme, just a buffer to store 1000 transform matrices. However, I don't know how to setup instancing in OSG. osgEarth does seem to support instancing but for ground cover, not arbitrary models.