Splitting two ObservableCollections lightning fast
NotifyCollectionChangedEventArgs

You receive a NotifyCollectionChangedEventArgs, wich holds interesting data about the items that got added NewItems and removed OldItems from the collection. There is also an Action that defines how the collection got modified. Your changed handler is implemented as a NotifyCollectionChangedAction.Reset handler. It doesn't take into account the information from the event e. 


var markers = ((IEnumerable&lt;XfxMarker&gt;) sender).ToList();



This means, you are checking all elements against the previously cached elements by the handler container class and manually verify which ones are added and which ones removed. Let's say filter is m =&gt; _map.MapBounds.Contains(m.CenterPoint):


 var markersToAdd = markers
    .Where(m =&gt; filter(m), out offScreen).Except(_oldMarkers,comparer);



and 


var markersToRemove = _oldMarkers.Except(markers, comparer).Union(offScreen);



However, in most cases I presume the action to be Add or Remove. If this is the case, you already have the new and/or old elements. For instance, on Add:

var markersAddedInSource = e.NewItems.OfType&lt;XfxMarker&gt;();
var markersToAdd = markersAddedInSource.Where(filter);
var markersToRemove = markersAddedInSource.Except(markersToAdd);


Or using your own extension method:

var markersAddedInSource = e.NewItems.OfType&lt;XfxMarker&gt;();
var markersToAdd = markersAddedInSource.Where(filter, out var markersToRemove);


This saves you a couple of iterations on the source collection and allows you to only iterate on the changed elements.



MapMarkerComparer

I don't know the type of Latitude and Longitude, but consider checking equality on their source type rather than their ToString outcome. 


x.CenterPoint.Latitude.ToString() == y.CenterPoint.Latitude.ToString()



In case you have issues with rounding or truncation (likely with float or double) you could round the values in the check.

x.CenterPoint.Latitude == y.CenterPoint.Latitude


Or you could use rounding:

Math.Round(x.CenterPoint.Latitude, precision) 
    == Math.Round(y.CenterPoint.Latitude, precision)


Or you could guard against a tolerance threshold:

Math.Abs(x.CenterPoint.Latitude - y.CenterPoint.Latitude) &lt; tolerance




IEnumerable&lt;T&gt; Where&lt;T&gt;

Consider changing the method signature to

public static ILookup&lt;bool, T&gt; Where&lt;T&gt;(this IEnumerable&lt;T&gt; input, Func&lt;T, bool&gt; predicate);


Or returning a Dictionary:

public static IDictionary&lt;bool, IEnumerable&lt;T&gt;&gt; Where&lt;T&gt;(
    this IEnumerable&lt;T&gt; input, Func&lt;T, bool&gt; predicate);


So you don't need to worry about a return value and an out-parameter. This pattern isn't used that much anymore and is mostly used to Try* operations.

You can get the items that matched the predicate: 

var results = markers.Where(filter);
var matched = results[true];
var unmatched = results[false];


Considering renaming Where to avoid confusion with LINQ's Where method.