#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cmath>
#include <utility>
using namespace std;
#define repeat(i,n) for (int i = 0; i < (n); ++i)

template <typename T>
class ford_fulkerson {
public:
    struct edge_t { int to; T cap; int rev; };
    static T infinity;

    ford_fulkerson() {}
    ford_fulkerson(int n) : graph(n) {}

    vector<vector<edge_t> > graph; // adjacency list

    int add_vertex() {
        graph.push_back(vector<edge_t>());
        return graph.size()-1;
    }

    void add_edge(int from, T to, int cap) {
        graph[from].push_back((edge_t){   to, cap, (int)graph[  to].size() });
        graph[  to].push_back((edge_t){ from,   0, (int)graph[from].size() - 1 });
    }

    vector<bool> used;

    T run_destructive(int s, int t) {
        T flow = 0;
        used.resize(graph.size());
        while (true) {
            fill(used.begin(), used.end(), false);
            T f = dfs(s, t, infinity);
            if (f == 0) return flow;
            flow += f;
        }
    }

    // search augmenting pathes from `v' to `t', returns the volume of water `f'
    T dfs(int v, int t, T f) {
        if (v == t) return f; // base case
        used[v] = true;
        repeat (i,(int)graph[v].size()) { // for each adjacent nodes which are not used and able to flow
            edge_t & e = graph[v][i];
            const int w = e.to;
            if (not used[w] and 0 < e.cap) {
                T d = dfs(w, t, min(f, e.cap)); // v -> w ->> t
                if (0 < d) { // let flow and make reverse capacity
                    e.cap -= d;
                    graph[w][e.rev].cap += d;
                    return d;
                }
            }
        }
        return 0; // path not found
    }
};
template<> int ford_fulkerson<int>::infinity = 1e9;
template<> long long ford_fulkerson<long long>::infinity = 1e18;
template<> double ford_fulkerson<double>::infinity = 1/(double)0;
int sq(int x) { return x*x; }
double hypot(int x, int y) { return sqrt(sq(x)+sq(y)); }
double distance(int ax, int ay, int bx, int by) { return hypot(ax-bx,ay-by); }
template <class T, class S>
double distance(const T & a, const S & b) { return hypot(a.x-b.x,a.y-b.y); }

#define MAX_N 100
#define MAX_M 100
struct troop_t { int x, y, v; };
struct base_t  { int x, y; };
double time(const troop_t & t, const base_t & b) { return distance(t,b) /(double) t.v; }
struct path_t { int n, m; double t; };
bool operator < (const path_t & a, const path_t & b) { return make_pair(a.t,make_pair(a.n,a.m)) < make_pair(b.t,make_pair(b.n,b.m)); }

int main() {
    while (true) {
        int N, M; cin >> N >> M;
        if (N == 0 or M == 0) break;
        static troop_t T[MAX_N]; repeat (n,N) cin >> T[n].x >> T[n].y >> T[n].v;
        static base_t  B[MAX_M]; repeat (m,M) cin >> B[m].x >> B[m].y;

        static path_t p[MAX_N*MAX_M];
        repeat (n,N) repeat (m,M) p[M*n+m] = (path_t){ n, m, time(T[n],B[m]) };
        sort(p, p+N*M);

        int low = M, high = N*M; // the answer is in [low,high]
        while (low < high) {
            int mid = (low + high) / 2;
            ford_fulkerson<int> mf(1+N+M+1);
            repeat (n,N)   mf.add_edge(0,        1+n,        1);
            repeat (i,mid) mf.add_edge(1+p[i].n, 1+N+p[i].m, 1);
            repeat (m,M)   mf.add_edge(1+N+m,    1+N+M,      1);
            int f = mf.run_destructive(0, 1+N+M);
            if (f < M) {
                low = mid+1; // the answer is in (mid,high]
            } else {
                high = mid;
            }
        }
        printf("%.8lf\n", p[low-1].t);
    }
    return 0;
}

Measured memory_bytes: 1708